# Visualization Library A lightweight C++ OpenGL middleware for 2D/3D graphics | [Home] [Tutorials] [All Classes] [Grouped Classes]

# Directional, Point and Spot Lights Tutorial

This tutorial demonstrates how to use the following light types: directional lights, point lights, spot lights, how to animate the position and direction of a light and how to attach a light to a camera.

[From `App_Lights.hpp`]

    class App_Lights: public BaseDemo
    {
    public:
      virtual void shutdown() {}

      virtual void initEvent()
      {
        BaseDemo::initEvent();

        mCameraLightType = Disable;

        // setup our simple scene with a plane and a few columns

        mLightFx = new vl::Effect;
        mLightFx->shader()->enable(vl::EN_LIGHTING); /* IMPORTANT! */
        mLightFx->shader()->enable(vl::EN_DEPTH_TEST);

        vl::ref<vl::Geometry> ground = vlut::makeGrid( vl::vec3(0,0,0), 20, 20, 100,100 );
        ground->computeNormals();
        sceneManager()->tree()->addActor(ground.get(), mLightFx.get());

        for(int i=0; i<9; ++i)
        {
          vl::ref<vl::Geometry> pillar = vlut::makeCylinder( vl::vec3(6,2,-4*1.1f + i*1.1f), 1, 4, 100, 100, true, false);
          pillar->computeNormals();
          sceneManager()->tree()->addActor(pillar.get(), mLightFx.get());
        }

        for(int i=0; i<5; ++i)
        {
          vl::ref<vl::Geometry> pillar = vlut::makeCylinder( vl::vec3(-3,2,-2*1.1f + i*1.1f), 1, 4, 100, 100, true, false);
          pillar->computeNormals();
          sceneManager()->tree()->addActor(pillar.get(), mLightFx.get());
        }

        // light setup

        // A typical OpenGL implementation supports up to 8 lights at the same time. It means that your scene can have
        // as many lights as you want but each object can be lit "only" by 8 lights at a time, this is usually never the
        // case as lighting absorbs considerable computational resources.

        // In our example each object will be lit up to 5 lights at the same time.

        mLight0 = new vl::Light(0);
        mLight1 = new vl::Light(1);
        mLight2 = new vl::Light(2);
        mLight3 = new vl::Light(3);
        mLight4 = new vl::Light(4);

        // Add the lights to the shader in order to use them (we save light #4 for the camera)
        mLightFx->shader()->setRenderState( mLight0.get() );
        mLightFx->shader()->setRenderState( mLight1.get() );
        mLightFx->shader()->setRenderState( mLight2.get() );
        mLightFx->shader()->setRenderState( mLight3.get() );

        // Define the light diffuse color.
        // See the OpenGL Programmer's Guide for more details about the OpenGL lighting model and equations.
        mLight0->setDiffuse(vlut::red);
        mLight1->setDiffuse(vlut::green);
        mLight2->setDiffuse(vlut::blue);
        mLight3->setDiffuse(vlut::white);
        mLight4->setDiffuse(vlut::white);

        // Setup lights #0, #1 and #2 as positional lights with constant, linear and quadratic attenuation.
        // Again, see the OpenGL Programmer's Guide for more details about the OpenGL lighting model and equations.
        mLight0->setPosition(vl::fvec4(0,0,0,1)); /* default */
        mLight0->setConstantAttenuation(1.0f);
        mLight0->setLinearAttenuation(0.0f);
        mLight0->setQuadraticAttenuation(0.0f);

        mLight1->setPosition(vl::fvec4(0,0,0,1)); /* default */
        mLight1->setConstantAttenuation(0.0f);
        mLight1->setLinearAttenuation(1.0f);
        mLight1->setQuadraticAttenuation(0.0f);

        mLight2->setPosition(vl::fvec4(0,0,0,1)); /* default */
        mLight2->setConstantAttenuation(0.0f);
        mLight2->setLinearAttenuation(0.0f);
        mLight2->setQuadraticAttenuation(1.0f);

        // Setup light #3 as a spot light.
        // Valid values are from 0.0f to 90.0f plus the special value 180.0f (default) which disables the spot lighting.
        mLight3->setSpotCutoff(45.0f);
        mLight3->setSpotExponent(5.0f);
        mLight3->setSpotDirection(vl::fvec3(1,0,0));
        // A spot light is essentially a special positional light so these apply to spot lights as well.
        mLight3->setConstantAttenuation(1.0f);
        mLight3->setLinearAttenuation(0.0f);
        mLight3->setQuadraticAttenuation(0.0f);

        // Setup light's Transform to animate them
        mLight0_Transform = new vl::Transform;
        mLight1_Transform = new vl::Transform;
        mLight2_Transform = new vl::Transform;
        mLight3_Transform = new vl::Transform;
        vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild(mLight0_Transform.get());
        vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild(mLight1_Transform.get());
        vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild(mLight2_Transform.get());
        vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild(mLight3_Transform.get());
        // light 0..3 follow the relative transform
        mLight0->followTransform(mLight0_Transform.get());
        mLight1->followTransform(mLight1_Transform.get());
        mLight2->followTransform(mLight2_Transform.get());
        mLight3->followTransform(mLight3_Transform.get());
        // light 4 follows the camera
        mLight4->followTransform(NULL);

        // add a white sphere for each light to have a better visual feedback of the light animation.
        vl::ref<vl::Geometry> bulb = vlut::makeUVSphere(vl::vec3(0,0,0), 0.5f);
        vl::ref<vl::Effect> bulb_fx = new vl::Effect;
        bulb_fx->shader()->enable(vl::EN_DEPTH_TEST);
        sceneManager()->tree()->addActor(bulb.get(), bulb_fx.get(), mLight0_Transform.get());
        sceneManager()->tree()->addActor(bulb.get(), bulb_fx.get(), mLight1_Transform.get());
        sceneManager()->tree()->addActor(bulb.get(), bulb_fx.get(), mLight2_Transform.get());
        sceneManager()->tree()->addActor(bulb.get(), bulb_fx.get(), mLight3_Transform.get());

        // Simple text to inform the user of the currently active light setup.
        vl::ref<vl::Effect> text_fx = new vl::Effect;
        text_fx->shader()->enable(vl::EN_BLEND);
        mText = new vl::Text;
        mText->setFont( vl::VisualizationLibrary::fontManager()->acquireFont("/font/bitstream-vera/VeraMono.ttf", 8) );
        mText->setAlignment( vl::AlignHCenter | vl::AlignTop );
        mText->setViewportAlignment( vl::AlignHCenter | vl::AlignTop );
        mText->translate(0,-10,0);
        sceneManager()->tree()->addActor(mText.get(), text_fx.get());
        updateText();
      }

      void updateText()
      {
        switch(mCameraLightType)
        {
          case Disable:          mText->setText("Camera light: Disable");           break;
          case DirectionalLight: mText->setText("Camera light: Directional Light"); break;
          case PositionalLight:  mText->setText("Camera light: Positional Light");  break;
          case SpotLight:        mText->setText("Camera light: Spot Light");        break;
        }
      }

      // Pressing space we cycle around 4 different light setup for light #4 (the one following the camera)
      void keyPressEvent(unsigned short ch, vl::EKey key)
      {
        BaseDemo::keyPressEvent(ch, key);
        if (key == vl::Key_Space)
        {
          mCameraLightType = (ECameraLightType)((mCameraLightType+1)%4);

          switch(mCameraLightType)
          {
          case Disable:
            // removes light #4 from the Shader
            mLightFx->shader()->eraseRenderState(mLight4.get());
            // you can also do:
            // mLightFx->shader()->removeRenderState(vl::RS_Light4);
            // or even
            // mLightFx->shader()->removeRenderState(vl::Light(4).type());
            break;
          case DirectionalLight:
            // add the light to the shader
            mLightFx->shader()->setRenderState(mLight4.get());
            // since the fourth component is 0 OpenGL considers this a direction
            mLight4->setPosition(vl::fvec4(0,0,1,0));
            // disable spot light
            mLight4->setSpotCutoff(180.0f);
            break;
          case PositionalLight:
            // add the light to the shader (just for clarity)
            mLightFx->shader()->setRenderState(mLight4.get());
            // since the fourth component is 1 OpenGL considers this a position
            mLight4->setPosition(vl::fvec4(0,0,0,1));
            // positional light attenuation
            mLight4->setConstantAttenuation(1.0f);
            mLight4->setLinearAttenuation(0.0f);
            mLight4->setQuadraticAttenuation(0.0f);
            // disable spot light
            mLight4->setSpotCutoff(180.0f);
            break;
          case SpotLight:
            // add the light to the shader (just for clarity)
            mLightFx->shader()->setRenderState(mLight4.get());
            // since the fourth component is 1 OpenGL considers this a position
            mLight4->setPosition(vl::fvec4(0,0,0,1));
            // positional light attenuation
            mLight4->setConstantAttenuation(1.0f);
            mLight4->setLinearAttenuation(0.0f);
            mLight4->setQuadraticAttenuation(0.0f);
            // enable and setup spot light
            mLight4->setSpotCutoff(25.0f);
            mLight4->setSpotExponent(40.0f);
            mLight4->setSpotDirection(vl::fvec3(0,0,-1));
            break;
          }
        }
        updateText();
      }

      // Animate the lights
      virtual void run()
      {
        vl::Real phase = 120.0f;
        vl::mat4 m;

        m.translate(5,1,0);
        m.rotate( phase*0.0f + 1.0f*vl::Time::currentTime()*vl::fPi*15, 0, 1.0f, 0);
        mLight0_Transform->setLocalMatrix(m);

        m.setIdentity();
        m.translate(5,1,0);
        m.rotate( phase*1.0f + 1.3f*vl::Time::currentTime()*vl::fPi*15, 0, 1.0f, 0);
        mLight1_Transform->setLocalMatrix(m);

        m.setIdentity();
        m.translate(5,1,0);
        m.rotate( phase*2.0f + 1.7f*vl::Time::currentTime()*vl::fPi*15, 0, 1.0f, 0);
        mLight2_Transform->setLocalMatrix(m);

        m.setIdentity();
        m.translate( sin(vl::Time::currentTime()*vl::fPi),0.5f,0);
        m.rotate( 0.5f*vl::Time::currentTime()*vl::fPi*15, 0, 1.0f, 0);
        mLight3_Transform->setLocalMatrix(m);
      }

    protected:
      vl::ref< vl::Light > mLight0;
      vl::ref< vl::Light > mLight1;
      vl::ref< vl::Light > mLight2;
      vl::ref< vl::Light > mLight3;
      vl::ref< vl::Light > mLight4;
      vl::ref< vl::Transform > mLight0_Transform;
      vl::ref< vl::Transform > mLight1_Transform;
      vl::ref< vl::Transform > mLight2_Transform;
      vl::ref< vl::Transform > mLight3_Transform;
      vl::ref< vl::Effect > mLightFx;
      vl::ref< vl::Text > mText;
      enum ECameraLightType { Disable, DirectionalLight, PositionalLight, SpotLight } mCameraLightType;
    };

    // 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.