Version 2 (modified by osg, 7 years ago)

Added menu

Using OpenGL Shading Language Vertex and Fragment Shaders (Introduction)

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

Goal

Replace the fixed-function pipeling lighting and texture calculations with an OpenGL Shading Language vertex and fragment program for selected nodes in the scene graph. (The procedural brick shader from 3Dlabs.)

Overview

OpenGL Shading Language allows programers to write custom pixel and vertex shaders. For more information on shading languages - including minimum hardware and software requirements - visit here. The osg::Program and osg::Shader module allow osg users to apply these shaders as part of a StateSet to selected subtrees within a scene graph. Using a custom vertex or fragment shader in OpenSceneGraph involves the following basic classes:

1. osg::Program - application level abstraction of the OpenGL Shading Language glProgramObject. The ProgramObject class is derived from the osg::StateAttribute class. This means that instance of the osg::Program class can be associated with StateSets and enabled using the StateSet class' setAttributeAndModes() method. Enabling a program object for a stateset results in drawables within that stateset being rendered using the Program's shaders. 2. osg::Shader - application level abstraction of the OpenGL Shading Language glShaderObject. This class manages loading and compiling shader source code. Instances of the osg::Shader class can be assigned to one or more osg::Program instances. There are two types of shader objects: osg::Shader::FRAGMENT and osg::Shader::VERTEX.

The code

The steps to create an application that uses an OpenGL pixel and fragment shader are as follows:

  • Create an osg::Program instance
  • Create VERTEX and FRAGMENT instances of the osg::Shader class
  • Load and compile the shader source
  • Add the shaders to the osg::Program instance
  • Associate and enable the osg::Program instance as part of a StateSet.

The code to load and apply a basic vertex and fragment shader is as follows:

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

       osg::Program* brickProgramObject = new osg::Program;
       osg::Shader* brickVertexObject = 
          new osg::Shader( osg::Shader::VERTEX );
       osg::Shader* brickFragmentObject = 
          new osg::Shader( osg::Shader::FRAGMENT );
       brickProgramObject->addShader( brickFragmentObject );
       brickProgramObject->addShader( brickVertexObject );
       loadShaderSource( brickVertexObject, "shaders/brick.vert" );
       loadShaderSource( brickFragmentObject, "shaders/brick.frag" );

       brickState->setAttributeAndModes(brickProgramObject, osg::StateAttribute::ON);

The convenience function to load the source code for a shader and associate the compiled code with a shader object instance is as follows:

    bool loadShaderSource(osg::Shader* obj, const std::string& fileName )
    {
       std::string fqFileName = osgDB::findDataFile(fileName);
       if( fqFileName.length() == 0 )
       {
          std::cout << "File \"" << fileName << "\" not found." << std::endl;
          return false;
       }
       bool success = obj->loadShaderSourceFromFile( fqFileName.c_str());
       if ( !success  )
       {
          std::cout << "Couldn't load file: " << fileName << std::endl;
          return false;
       }
       else
       {
          return true;
       }
    }