Visualization Library v1.0.3

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.

pagGuideBillboards.jpg

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
    vl::ref<vl::Effect> effect = new vl::Effect;
    // 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()->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()->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
      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
      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
    vl::ref<vl::Effect> effect = new vl::Effect;
    // 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()->enable(vl::EN_DEPTH_TEST);
    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
      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
      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
    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(vl::orange);
    effect->shader()->enable(vl::EN_DEPTH_TEST);

    // 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
    vl::ref<vl::Geometry> quad = new vl::Geometry;
    // quad vertices
    vl::ref<vl::ArrayFloat3> vert = new vl::ArrayFloat3;
    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::ArrayFloat2> texc = new vl::ArrayFloat2;
    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()
  {
    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 = vl::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;
    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);
    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 updateScene()
  {
    mArm1Transform->setLocalMatrix( 
      vl::mat4::getRotation(vl::Time::currentTime()*45,0,1,0) * 
      vl::mat4::getTranslation(0.0f,2.0f,0.0f)
    );
  }

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

// Have fun!


Visualization Library v1.0.3 Reference Documentation
Copyright Michele Bosi. All rights reserved.
Updated on Tue Feb 7 2017 00:55:04.
Permission is granted to use this page to write and publish articles regarding Visualization Library.