Version 1 (modified by osg, 7 years ago)

Initial copy of NPS Version

Passing variables to shaders

Goal

Use an OpenGL shader that requires parameters to be passed from the calling program to the shader.

Overview

Shaders have two types of parameters. Uniform variables represent values that are consistent for a frame. These typically include parameters such as viewer direction and light position. Varying data changes for each unit processed (vertices in vertex shaders and fragments in fragment shaders.) Parameters are passed from the rendering application to shaders using uniform parameters. In Randi Rost's bump mapping shaders there are three uniform parameters: a vec3 'LightPosition?' and two sampler2D variables - 'baseTexture' and 'normalMap'. The osgGL2 classes provide a straightforward mechanism for an OpenSceneGraph application to pass values to these shaders.

The code

Here's how to pass parameters from an OpenSceneGraph program to bump mapping shaders. This code makes use of two methods of the ProgramObject class. The setUniform method takes two arguments: a string that matches a uniform parameter declared in a shader and a value for that shader parameter. Similarly, the setSampler takes a string that matches a sampler2D variable name declared in a shader and an integer cooresponding to a texure unit. The code to pass variables required by the bump map shaders is as follows:

    bool loadShaderSource(osg::Shader* obj, const std::string& fileName );

    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) ) );

       // attach an "empty" ProgramObject to the rootNode, which will act as
       // the default StateAttribute. An empty ProgramObject (ie without any
       // attached ShaderObjects) is a special case, which means to use the
       // OpenGL 1.x "fixed functionality" rendering pipeline.
       osg::StateSet* rootState = rootNode->getOrCreateStateSet();
       osg::Program* defaultProgramObject = new osg::Program;
       rootState->setAttributeAndModes(defaultProgramObject, osg::StateAttribute::ON);
       
       osg::StateSet* bumpMapState = tankNode->getOrCreateStateSet();

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

       osg::Shader* brickVertexObject = 
          new osg::Shader( osg::Shader::VERTEX );
       osg::Shader* brickFragmentObject = 
          new osg::Shader( osg::Shader::FRAGMENT );

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

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

       bumpMapProgramObject->addShader( brickFragmentObject );
       bumpMapProgramObject->addShader( brickVertexObject );

       //bumpMapProgramObject->setUniform("LightPosition", osg::Vec3(0,0,1));
       //bumpMapProgramObject->setSampler("normalMap",1);
       //bumpMapProgramObject->setSampler("baseTexture",0);

       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);
       
       bumpMapState->setAttributeAndModes(bumpMapProgramObject, osg::StateAttribute::ON);

       osg::Texture2D* tankBodyNormalMap = new osg::Texture2D;
       // protect from being optimized away as static state:
       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;
    }