# Visualization Library A lightweight C++ OpenGL middleware for 2D/3D graphics | [Home] [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.hpp`]

    class App_Billboards: public BaseDemo
    {
    public:
      // initialization
      void initEvent()
      {
        BaseDemo::initEvent();
        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
        vl::ref<vl::Effect> effect = new vl::Effect;
        effect->shader()->setRenderState( new vl::Light(0) );
        effect->shader()->enable(vl::EN_BLEND);
        effect->shader()->enable(vl::EN_DEPTH_TEST);
        effect->shader()->gocDepthMask()->set(false);
        effect->shader()->enable(vl::EN_LIGHTING);
        effect->shader()->gocLight(0)->setLinearAttenuation(0.025f);
        effect->shader()->gocTextureUnit(0)->setTexture( new vl::Texture("images/tree.png") );

        vl::ref<vl::Geometry> tree = generateQuad();
        tree->transform( vl::mat4::translation(0,+0.5f,0) );
        tree->transform( vl::mat4::scaling(1.0f,+2.0f,1.0f) );
        tree->computeNormals();

        for(int i=0; i<tree_count; i++)
        {
          // new billboard
          vl::ref<vl::Billboard> billboard = new vl::Billboard;
          // set axis-aligned billboard type: rotate the billboard towards the camera but only around the specified axis.
          billboard->setType(vl::BT_AxisAlignedBillboard);
          billboard->setAxis(vl::vec3(0,1.0f,0));
          // bind billboard to the transform tree
          vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild(billboard.get());
          // generate a random position
          vl::Real x = vl::randomMinMax(-side/2.0f,+side/2.0f);
          vl::Real y = 0;
          vl::Real z = vl::randomMinMax(-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
        vl::ref<vl::Effect> effect = new vl::Effect;
        effect->shader()->setRenderState( new vl::Light(0) );
        effect->shader()->enable(vl::EN_BLEND);
        effect->shader()->enable(vl::EN_DEPTH_TEST);
        effect->shader()->gocTextureUnit(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
          vl::ref<vl::Billboard> billboard = new vl::Billboard;
          // set spherical billboard type: orient the object always towards the camera.
          billboard->setType(vl::BT_SphericalBillboard);
          // add billboard to the transform tree
          vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild(billboard.get());
          // compute a random point on the skydome
          vl::Real x = vl::randomMinMax(-1.0f,+1.0f);
          vl::Real y = vl::randomMinMax(0,2.0f);
          vl::Real z = vl::randomMinMax(-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
        vl::ref<vl::Effect> effect = new vl::Effect;
        effect->shader()->setRenderState( new vl::Light(0) );
        effect->shader()->enable(vl::EN_LIGHTING);
        effect->shader()->gocLight(0)->setLinearAttenuation(0.025f);
        effect->shader()->gocMaterial()->setDiffuse(vlut::orange);
        effect->shader()->enable(vl::EN_DEPTH_TEST);

        // use a simple plane to render the ground
        vl::ref<vl::Geometry> geom = vlut::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
        vl::ref<vl::Geometry> quad = new vl::Geometry;
        // quad vertices
        vl::ref<vl::ArrayFVec3> vert = new vl::ArrayFVec3;
        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
        vl::ref<vl::ArrayFVec2> texc = new vl::ArrayFVec2;
        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->primitives()->push_back( new vl::DrawArrays(vl::PT_QUADS,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()
      {
        vl::ref<vl::Effect> effect = new vl::Effect;
        effect->shader()->setRenderState( new vl::Light(0) );
        effect->shader()->enable(vl::EN_BLEND);
        effect->shader()->enable(vl::EN_DEPTH_TEST);
        effect->shader()->enable(vl::EN_LIGHTING);

        vl::ref<vl::Geometry>  arm_g = vlut::makeCylinder(vl::vec3(0,0,0), 0.5f, 4.0f);
        arm_g->computeNormals();
        vl::ref<vl::Transform> arm1_t = new vl::Transform;
        vl::ref<vl::Transform> arm2_t = new vl::Transform;
        vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild(arm1_t.get());
        arm1_t->addChild(arm2_t.get());
        arm2_t->setLocalMatrix(vl::mat4::translation(0.0f,2.0f,0.0f) * vl::mat4::rotation(90,0,0,1) * vl::mat4::translation(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 = vlut::makeBox(vl::vec3(0,-0.75f,0), 1, 1, 1);
        box->computeNormals();

        // the billboard
        vl::ref<vl::Billboard> billboard = new vl::Billboard;
        // use an axis aligned billboard
        billboard->setType(vl::BT_AxisAlignedBillboard);
        // 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 run()
      {
        mArm1Transform->setLocalMatrix(
          vl::mat4::rotation(vl::Time::currentTime()*45,0,1,0) *
          vl::mat4::translation(0.0f,2.0f,0.0f)
        );
      }

    protected:
      vl::ref<vl::Transform> mArm1Transform;
    };

    // Have fun!

Visualization Library v2009.08 Reference Documentation
Copyright 2005-2009 Michele Bosi. All rights reserved.
Updated on Sun Nov 8 14:28:52 2009.
Permission is granted to use this page to write and publish articles regarding Visualization Library.