Version 2 (modified by osg, 7 years ago)

Added menu

Updating shaders

(Wiki editing note: Code needs conversion to osgViewer)
(Wiki editing note: Add link to complete source code at bottom)

Goal

Continually update the parameters passed to an OpenGL shader program.

Overview

This tutorial demonstrates the procedures to continually update the parameters of an OpenGL shader program to control dynamic shader effects for objects in a scene. We'll take the BumpMap shader and update its LightPosition uniform variable to simulate the appearance of a light being moved around our bump-mapped model. To do this we'll use the same setUniform method introduced in the previous tutorial. This time however, the update call will be made from within an update callback. The body of the class will be an update method that changes the vector that represents the position of the light. To use the class, we'll simply make this callback one of the update callbacks for a node attached to our scene.

The code

The shader update callback definition would be as follows:

    class updateBumpShader : public osg::Uniform::Callback
    {
    public:
       virtual void operator() 
          ( osg::Uniform* uniform, osg::NodeVisitor* nv )
       {
          float angle = nv->getFrameStamp()->getReferenceTime();
          float x = sinf(angle)*.2f;
          float y = cosf(angle)*.2f;
          osg::Vec3 lightPos(x,y,.6f);
          uniform->set(lightPos);
       }
    };

The remainder of the code is primarily the same as the previous tutorial: setting up the scene and shader program instance. The only difference is that we need to make an instance of the above callback one of the updateCallbacks for a node in our scene.

    int main( int argc, char **argv )
    {
       osg::Group* rootNode = new osg::Group();
       osg::Node* tankNode = NULL;
       osg::Node* terrainNode = NULL;
       osg::PositionAttitudeTransform* tankXform = 
          new osg::PositionAttitudeTransform();

       osgDB::FilePathList pathList = osgDB::getDataFilePathList();
       pathList.push_back
          ("C:\\Projects\\OpenSceneGraph\\OpenSceneGraph-
             Data\\NPSData\\Models\\T72-Tank\\");
       pathList.push_back
          ("C:\\Projects\\OpenSceneGraph\\OpenSceneGraph-
             Data\\NPSData\\Models\\JoeDirt\\");
       osgDB::setDataFilePathList(pathList);

       tankNode = osgDB::readNodeFile("T72-tank_des.flt");
       terrainNode = osgDB::readNodeFile("JoeDirt.flt");
       if (! (tankNode && terrainNode))
       {
          std::cout << "Couldn't load models, quitting." << std::endl;
          return -1;
       }
       rootNode->addChild(terrainNode);
       rootNode->addChild(tankXform);
       tankXform->addChild(tankNode);

       tankXform->setPosition( osg::Vec3(10,10,8) );
       tankXform->setAttitude( 
          osg::Quat(osg::DegreesToRadians(-45.0), osg::Vec3(0,0,1) ) );

       
       osg::StateSet* bumpMapState = tankNode->getOrCreateStateSet();

       osg::Program* bumpMapProgramObject = new osg::Program;

       osg::Shader* bumpVertexObject = 
          new osg::Shader( osg::Shader::VERTEX );
       osg::Shader* bumpFragmentObject = 
          new osg::Shader( osg::Shader::FRAGMENT );

       bool ok =
          loadShaderSource( bumpVertexObject, "shaders/bumpmap.vert" )
          &&
          loadShaderSource
             ( bumpFragmentObject, "shaders/bumpmap.frag" ) ;

       if(!ok)
       {
          std::cout << "Couldn't load shaders" << std::endl;
          return -1;
       }

       bumpMapProgramObject->addShader( bumpFragmentObject );
       bumpMapProgramObject->addShader( bumpVertexObject );

       osg::Uniform* lightPosU = 
          new osg::Uniform("LightPosition",osg::Vec3(0,0,1));
       osg::Uniform* normalMapU = new osg::Uniform("normalMap",1);
       osg::Uniform* baseTextureU = new osg::Uniform("baseTexture",0);

       bumpMapState->addUniform(lightPosU);
       bumpMapState->addUniform(normalMapU);
       bumpMapState->addUniform(baseTextureU);
       lightPosU->setUpdateCallback(new updateBumpShader() );

       bumpMapState->setAttributeAndModes
          (bumpMapProgramObject, osg::StateAttribute::ON);

       osg::Texture2D* tankBodyNormalMap = new osg::Texture2D;
       tankBodyNormalMap->setDataVariance(osg::Object::DYNAMIC); 
       osg::Image* tankBody = osgDB::readImageFile("TankBump.png");
       if (!tankBody)
       {
          std::cout << " couldn't find texture, quiting." << std::endl;
          return -1;
       }

       tankBodyNormalMap->setImage(tankBody);
       bumpMapState->setTextureAttributeAndModes
          (1,tankBodyNormalMap,osg::StateAttribute::ON);

       osgProducer::Viewer viewer;
       viewer.setUpViewer( osgProducer::Viewer::STANDARD_SETTINGS );
       viewer.setSceneData( rootNode );
       viewer.realize();

       while( !viewer.done() )
       {
          viewer.sync();
          viewer.update();
          viewer.frame();
       }
       viewer.sync();
       return 0;
    }