Visualization Library 2.0.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
Billboards Tutorial

This tutorial demonstrates how to use the vl::Billboard class to orient objects towards the camera.

The term "billboard" in computer graphics refers to an object that always faces the camera. Visualization Library supports two kinds of billboards, "spherical billboards" and "axis aligned billboards". The first kind is free to rotate in any direction and always faces the camera perfectly. The second kind rotates towards the camera but only around a specific rotation axis. This last kind is often used to create billboard trees which should face the camera but whose rotational axis must remain parallel to the world Y axis.

The following tutorial implements a simple forest using axis aligned billboards. To make the scene more interesting we will also add stars on the sky using spherical billboards. At the center of the scene we will also put a cube whose transform is a billboard attached to an animated transform tree. This demonstrates how billboards behave when attached to animated transform trees. When running the demo note how the cube keeps facing the camera while following its transform's parent's rotation.

[From App_Billboards.cpp]

class App_Billboards: public BaseDemo
{
public:
// initialization
void initEvent()
{
vl::Log::notify(appletInfo());
srand((unsigned int)time(NULL));
generateTrees (100.0f, 500);
generateStars (100.0f, 500);
generateGround(100.0f);
generateLunapark();
}
void generateTrees(float side, int tree_count)
{
// simple effect to render a tree billboard
// speedup tip: this allows VL to batch all the trees together greatly speeding up the rendering and avoiding switching textures back and forth with the stars.
effect->setRenderRank(2);
effect->shader()->setRenderState( new vl::Light, 0 );
effect->shader()->enable(vl::EN_BLEND);
effect->shader()->gocDepthMask()->set(false);
effect->shader()->gocLight(0)->setLinearAttenuation(0.025f);
effect->shader()->gocTextureSampler(0)->setTexture( new vl::Texture("images/tree.png") );
vl::ref<vl::Geometry> tree = generateQuad();
tree->transform( vl::mat4::getTranslation(0,+0.5f,0) );
tree->transform( vl::mat4::getScaling(1.0f,+2.0f,1.0f) );
tree->computeNormals();
for(int i=0; i<tree_count; i++)
{
// new billboard
// set axis-aligned billboard type: rotate the billboard towards the camera but only around the specified axis.
billboard->setAxis(vl::vec3(0,1.0f,0));
// bind billboard to the transform tree
rendering()->as<vl::Rendering>()->transform()->addChild(billboard.get());
// generate a random position
vl::real x = vl::random(-side/2.0f,+side/2.0f);
vl::real y = 0;
vl::real z = vl::random(-side/2.0f,+side/2.0f);
billboard->setPosition( vl::vec3(x,y,z) );
// add the tree actor
sceneManager()->tree()->addActor(tree.get(), effect.get(), billboard.get());
}
}
void generateStars(float side, int star_count)
{
// simple effect to render a star billboard
// speedup tip: this allows VL to batch all the stars together greatly speeding up the rendering and avoiding switching textures back and forth with the trees.
effect->setRenderRank(1);
effect->shader()->enable(vl::EN_BLEND);
effect->shader()->gocTextureSampler(0)->setTexture( new vl::Texture("images/sun.png", vl::TF_RGBA, true) );
vl::ref<vl::Geometry> star = generateQuad();
for(int i=0; i<star_count; i++)
{
// new billboard
// set spherical billboard type: orient the object always towards the camera.
// add billboard to the transform tree
rendering()->as<vl::Rendering>()->transform()->addChild(billboard.get());
// compute a random point on the skydome
vl::real x = vl::random(-1.0f,+1.0f);
vl::real y = vl::random(0,2.0f);
vl::real z = vl::random(-1.0f,+1.0f);
vl::vec3 n(x,y,z);
n.normalize();
n = n * sqrt(side*side/2.0f);
n.y() *= 0.2f;
// set the billboard position and rotation center.
billboard->setPosition( n );
// add the star actor
sceneManager()->tree()->addActor(star.get(), effect.get(), billboard.get());
}
}
// generates the ground
void generateGround(float side)
{
// orange effect using lighting
effect->shader()->setRenderState( new vl::Light, 0 );
effect->shader()->gocLight(0)->setLinearAttenuation(0.025f);
effect->shader()->gocMaterial()->setDiffuse(vl::orange);
// use a simple plane to render the ground
vl::ref<vl::Geometry> geom = vl::makeGrid(vl::vec3(0,0,0), side, side, 200, 200);
geom->computeNormals();
sceneManager()->tree()->addActor( geom.get(), effect.get(), NULL);
}
// returns a Geometry that renders a single quad
vl::ref<vl::Geometry> generateQuad()
{
// geometry
// quad vertices
vert->resize( 4 );
quad->setVertexArray(vert.get());
vert->at(0) = vl::fvec3( -0.5f, -0.5f, 0.0f );
vert->at(1) = vl::fvec3( +0.5f, -0.5f, 0.0f );
vert->at(2) = vl::fvec3( +0.5f, +0.5f, 0.0f );
vert->at(3) = vl::fvec3( -0.5f, +0.5f, 0.0f );
// texture coords
texc->resize( 4 );
quad->setTexCoordArray(0,texc.get());
texc->at(0) = vl::fvec2( 0.0f, 0.0f );
texc->at(1) = vl::fvec2( 1.0f, 0.0f );
texc->at(2) = vl::fvec2( 1.0f, 1.0f );
texc->at(3) = vl::fvec2( 0.0f, 1.0f );
// quad primitive
quad->drawCalls().push_back( new vl::DrawArrays(vl::PT_TRIANGLE_FAN, 0, 4) );
return quad;
}
// This function demonstrates how a billboard behaves when it is put under an animated transform hierarchy.
// Note how the cube always faces the camera even if it follows its parent's rotation.
void generateLunapark()
{
effect->shader()->setRenderState( new vl::Light, 0 );
effect->shader()->enable(vl::EN_BLEND);
vl::ref<vl::Geometry> arm_g = vl::makeCylinder(vl::vec3(0,0,0), 0.5f, 4.0f);
arm_g->computeNormals();
rendering()->as<vl::Rendering>()->transform()->addChild(arm1_t.get());
arm1_t->addChild(arm2_t.get());
arm2_t->setLocalMatrix(vl::mat4::getTranslation(0.0f,2.0f,0.0f) * vl::mat4::getRotation(90,0,0,1) * vl::mat4::getTranslation(0.0f,2.0f,0.0f));
sceneManager()->tree()->addActor( arm_g.get(), effect.get(), arm1_t.get());
sceneManager()->tree()->addActor( arm_g.get(), effect.get(), arm2_t.get());
vl::ref<vl::Geometry> box = vl::makeBox(vl::vec3(0,-0.75f,0), 1, 1, 1);
// the billboard
// use an axis aligned billboard
// the axis is always in world coordinates
billboard->setAxis(vl::vec3(0,1,0));
// remember that "position" is relative to the billboard's parent's coordinates
billboard->setPosition(0,2,0);
// add the billboard to its transform parent
arm2_t->addChild(billboard.get());
// add the box actor
sceneManager()->tree()->addActor( box.get(), effect.get(), billboard.get());
// to be animated below
mArm1Transform = arm1_t;
}
// animate the lunapark
void updateScene()
{
mArm1Transform->setLocalMatrix(
vl::mat4::getTranslation(0.0f,2.0f,0.0f)
);
}
protected:
vl::ref<vl::Transform> mArm1Transform;
};
// Have fun!