





















































In the 3D world, a billboard is a 2D image that is always facing a designated direction. Applications can use billboard techniques to create many kinds of special effects, such as explosions, fares, sky, clouds, and trees. In fact, any object can be treated as a billboard with itself cached as the texture, while looking from a distance. Thus, the implementation of billboards becomes one of the most popular techniques, widely used in computer games and real-time visual simulation programs.
The osg::BillBoard class is used to represent a list of billboard objects in a 3D scene. It is derived from osg::Geode, and can orient all of its children (osg::Drawable objects) to face the viewer's viewpoint. It has an important method, setMode(), that is used to determine the rotation behavior, which must set one of the following enumerations as the argument
UsagePOINT_ROT_EYEIf all drawables are rotated about the viewer position with the object coordinate Z axis constrained to the window coordinate Y axis.POINT_ROT_WORLDIf drawables are rotated about the viewer directly from their original orientation to the current eye direction in the world space.AXIAL_ROTIf drawables are rotated about an axis specified by setAxis().
Every drawable in the osg::BillBoard node should have a pivot point position, which is specified via the overloaded addDrawable() method, for example:
billboard->addDrawable( child, osg::Vec3(1.0f, 0.0f, 0.0f) );
All drawables also need a unified initial front face orientation, which is used for computing rotation values. The initial orientation is set by the setNormal() method. And each newly-added drawable must ensure that its front face orientation is in the same direction as this normal value; otherwise the billboard results may be incorrect.
The prerequisite for implementing billboards in OSG is to create one or more quad geometries first. These quads are then managed by the osg::BillBoard class. This forces all child drawables to automatically rotate around a specified axis, or face the viewer. These can be done by presetting a unified normal value and rotating each billboard according to the normal and current rotation axis or viewing vector.
We will create two banks of OSG banners, arranged in a V, to demonstrate the use of billboards in OSG. No matter where the viewer is and how he manipulates the scene camera, the front faces of banners are facing the viewer all the time. This feature can then be used to represent textured trees and particles in user applications.
#include <osg/Billboard> #include <osg/Texture2D> #include <osgDB/ReadFile> #include <osgViewer/Viewer>
osg::Geometry* createQuad()
{
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
osg::ref_ptr<osg::Image> image =
osgDB::readImageFile( "Images/osg256.png" );
texture->setImage( image.get() );
osg::ref_ptr<osg::Geometry> quad=
osg::createTexturedQuadGeometry(
osg::Vec3(-0.5f, 0.0f,-0.5f),
osg::Vec3(1.0f,0.0f,0.0f),
osg::Vec3(0.0f,0.0f,1.0f) );
osg::StateSet* ss = quad->getOrCreateStateSet()
ss->setTextureAttributeAndModes( 0, texture.get() );
return quad.release();
}
osg::ref_ptr<osg::Billboard> geode = new osg::Billboard;
geode->setMode( osg::Billboard::POINT_ROT_EYE );
osg::Geometry* quad = createQuad();
for ( unsigned int i=0; i<10; ++i )
{
float id = (float)i;
geode->addDrawable( quad, osg::Vec3(-2.5f+0.2f*id, id, 0.0f)
);
geode->addDrawable( quad, osg::Vec3( 2.5f-0.2f*id, id, 0.0f)
);
}
osg::StateSet* ss = geode->getOrCreateStateSet(); ss->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
osgViewer::Viewer viewer;
viewer.setSceneData( geode.get() );
return viewer.run();
The basic usage of billboards in OSG scene graph is shown in this example. But it is still possible to be further improved. All the banner geometries here are created with the createQuad() function, which means that the same quad and the same texture are reallocated at least 20 times! The object sharing mechanism is certainly an optimization here. Unfortunately, it is not clever enough to add the same drawable object to osg::Billboard with different positions, which could cause the node to work improperly. What we could do is to create multiple quad geometries that share the same texture object. This will highly reduce the video card's texture memory occupancy and the rendering load.
Another possible issue is that somebody may require loaded nodes to be rendered as billboards, not only as drawables. A node can consist of different kinds of child nodes, and is much richer than a basic shape or geometry mesh. OSG also provides the osg::AutoTransform class, which automatically rotates an object's children to be aligned with screen coordinates.
Billboards are widely used for simulating massive trees and plants. One or more tree pictures with transparent backgrounds are applied to quads of different sizes, and then added to the billboard node. These trees will automatically face the viewer, or to be more real, rotate about an axis as if its branches and leaves are always at the front. Now let's try to create some simple billboard trees. We only need to prepare an image nice enough.
Text is one of the most important components in all kinds of virtual reality programs. It is used everywhere—for displaying stats on the screen, labeling 3D objects, logging, and debugging. Texts always have at least one font to specify the typeface and qualities, as well as other parameters, including size, alignment, layout (left-to-right or right-to-left), and resolution, to determine its display behaviors. OpenGL doesn't directly support the loading of fonts and displaying texts in 3D space, but OSG provides full support for rendering high quality texts and configuring different text attributes, which makes it much easier to develop related applications.
The osgText library actually implements all font and text functionalities. It requires the osgdb_freetype plugin to work properly. This plugin can load and parse TrueType fonts with the help of FreeType, a famous third-party dependency. After that, it returns an osgText::Font instance, which is made up of a complete set of texture glyphs. The entire process can be described with the osgText::readFontFile() function.
The osgText::TextBase class is the pure base class of all OSG text types. It is derived from osg::Drawable, but doesn't support display lists by default. Its subclass, osgText::Text, is used to manage fat characters in the world coordinates. Important methods includes setFont(), setPosition(), setCharacterSize(), and setText(), each of which is easy to understand and use, as shown in the following example.
This time we are going to display a Cessna in the 3D space and provide descriptive texts in front of the rendered scene. A heads-up display (HUD) camera can be used here, which is rendered after the main camera, and only clears the depth buffer for directly updating texts to the frame buffer. The HUD camera will then render its child nodes in a way that is always visible.
#include <osg/Camera> #include <osgDB/ReadFile> #include <osgText/Font> #include <osgText/Text> #include <osgViewer/Viewer>
osg::ref_ptr<osgText::Font> g_font =
osgText::readFontFile("fonts/arial.ttf");
setAllowEventFocus() and setReferenceFrame() methods:
osg::Camera* createHUDCamera( double left, double right,
double bottom, double top )
{
osg::ref_ptr<osg::Camera> camera = new osg::Camera;
camera->setReferenceFrame( osg::Transform::ABSOLUTE_RF );
camera->setClearMask( GL_DEPTH_BUFFER_BIT );
camera->setRenderOrder( osg::Camera::POST_RENDER );
camera->setAllowEventFocus( false );
camera->setProjectionMatrix(
osg::Matrix::ortho2D(left, right, bottom, top) );
return camera.release();
}
osgText::Text* createText( const osg::Vec3& pos,
const std::string& content,
float size )
{
osg::ref_ptr<osgText::Text> text = new osgText::Text;
text->setFont( g_font.get() );
text->setCharacterSize( size );
text->setAxisAlignment( osgText::TextBase::XY_PLANE );
text->setPosition( pos );
text->setText( content );
return text.release();
}
osg::ref_ptr<osg::Geode> textGeode = new osg::Geode;
textGeode->addDrawable( createText(
osg::Vec3(150.0f, 500.0f, 0.0f),
"The Cessna monoplane",
20.0f)
);
textGeode->addDrawable( createText(
osg::Vec3(150.0f, 450.0f, 0.0f),
"Six-seat, low-wing and twin-engined",
15.0f)
);
osg::Camera* camera = createHUDCamera(0, 1024, 0, 768);
camera->addChild( textGeode.get() );
camera->getOrCreateStateSet()->setMode(
GL_LIGHTING, osg::StateAttribute::OFF );
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild( osgDB::readNodeFile("cessna.osg") );
root->addChild( camera );
osgViewer::Viewer viewer;
viewer.setSceneData( root.get() );
return viewer.run();
In the rendering window, you will see two lines of text over the Cessna model. No matter how you translate, rotate, or scale on the view matrix, the HUD texts will never be covered. Thus, users can always read the most important information directly, without looking away from their usual perspectives:
To build the example code with CMake or other native compilers, you should add the osgText library as dependence, and include the osgParticle, osgShadow, and osgFX libraries.
Here we specify the font from the arial.ttf file. This is a default font in most Windows and UNIX systems, and can also be found in OSG data paths. As you can see, this kind of font offers developers highly-precise displayed characters, regardless of font size settings. This is because the outlines of TrueType fonts are made of mathematical line segments and Bezier curves, which means they are not vector fonts. Bitmap (raster) fonts don't have such features and may sometimes look ugly when resized. Disable setFont() here, to force osgText to use a default 12x12 bitmap font. Can you figure out the difference between these two fonts?
The setText() method of osgText::Text accepts std::string variables directly. Meanwhile, it also accepts wide characters as the input argument. For example:
wchar_t* wstr = …; text->setText( wstr );
This makes it possible to support multi-languages, for instance, Chinese and Japanese characters. Now, try obtaining a sequence of wide characters either by defining them directly or converting from multi-byte characters, and apply them to the osgText::Text object, to see if the language that you are interested in can be rendered. Please note that the font should also be changed to support the corresponding language.