void WaterSurfaceShader::linkStateSet( osg::ref_ptr< osg::StateSet > stateSet )
{           
    std::stringstream ss;
             
    // Vertex Textures
    for( unsigned int iLevel = 0; iLevel < NumHeightMapLevels; iLevel++ )
    {
        int level = (int)iLevel;
        
        ss << VertexTextureLocation << level;
        std::string vertexTextureLocation = ss.str();        
        ss.str( "" );
        
        int pixelSize = (int)pow( 2.0, (double)( level + 1 ) );    
                     
        stateSet->setTextureAttributeAndModes( level, new NoiseTexture3D( pixelSize ), osg::StateAttribute::ON );
        
        osg::Uniform* uniform = new osg::Uniform( vertexTextureLocation.c_str(), level );     
        stateSet->addUniform( uniform );        
    }
            
    // Time
    _timeUniform = new osg::Uniform( osg::Uniform::FLOAT, TimeLocation.c_str() );
    _timeUniform->set( 0.0f );
    _timeUniform->setUpdateCallback( new TimeUniformCallback() );
    stateSet->addUniform( _timeUniform );
        
    // Link
    stateSet->setAttributeAndModes( _shaderProgram, osg::StateAttribute::ON );    
}
Пример #2
0
void ComputeNode::addComputationResultsRenderTree()
{

    _computationResultsRenderProgram = new osg::Program;

    _vertexShader = osgDB::readRefShaderFile(osg::Shader::VERTEX, _vertexShaderSourcePath);
    _computationResultsRenderProgram->addShader(_vertexShader.get());

    _geometryShader = osgDB::readRefShaderFile(osg::Shader::GEOMETRY, _geometryShaderSourcePath);
    _computationResultsRenderProgram->addShader(_geometryShader.get());

    _fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, _fragmentShaderSourcePath);
    _computationResultsRenderProgram->addShader(_fragmentShader.get());


    _computationResultsRenderProgram->addBindAttribLocation("tex_coords", 1);

    _computationResultsRenderGroup = new osg::Group;
    _computationResultsRenderGroup->setDataVariance(osg::Object::DYNAMIC);
    _computationResultsRenderStateSet = _computationResultsRenderGroup->getOrCreateStateSet();
    _computationResultsRenderStateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);

    osg::PointSprite *sprite = new osg::PointSprite;
    int texture_unit = 0;
    _computationResultsRenderStateSet->setTextureAttributeAndModes(texture_unit, sprite, osg::StateAttribute::ON);
    _computationResultsRenderStateSet->setAttributeAndModes(_computationResultsRenderProgram.get(), osg::StateAttribute::ON);
    _computationResultsRenderStateSet->addUniform(new osg::Uniform("particleTexture", texture_unit));
    _computationResultsRenderStateSet->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_X));
    _computationResultsRenderStateSet->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_Y));


    _computationResultsRenderStateSet->setMode(GL_POINT_SMOOTH, osg::StateAttribute::ON);
    _computationResultsRenderStateSet->setMode(GL_VERTEX_PROGRAM_POINT_SIZE_ARB, osg::StateAttribute::ON);
    _computationResultsRenderStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
    _computationResultsRenderStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

    osg::Texture2D *tex = new osg::Texture2D();

    osg::Image* particleImage = createSpotLightImage(osg::Vec4(1, 0, 0, 1), osg::Vec4(0.5, 0, 0, 0.0), 32, 0.7);
    if (particleImage)
    {
        tex->setImage(particleImage);
    }
    _computationResultsRenderStateSet->setTextureAttributeAndModes(texture_unit, tex, osg::StateAttribute::ON);


    osg::BlendFunc *blend = new osg::BlendFunc;
    if (false) //emissive particles
    {
        blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE);
    }
    else
    {
        blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
    }

    _computationResultsRenderStateSet->setAttributeAndModes(blend, osg::StateAttribute::ON);


    osg::Depth* depth = new osg::Depth;
    depth->setRange(0.0f, 0.0f);
    depth->setFunction(osg::Depth::ALWAYS);
    depth->setWriteMask(false);
    depth->setFunction(osg::Depth::ALWAYS);

    _computationResultsRenderStateSet->setAttributeAndModes(depth, osg::StateAttribute::OFF);


    osg::Geode* particleGeode = new osg::Geode;
    unsigned int numVertices = NUM_ELEMENTS_X*NUM_ELEMENTS_Y;

    osg::Geometry* particleGeometry = new osg::Geometry;
    particleGeometry->setUseDisplayList(false);
    particleGeometry->setUseVertexBufferObjects(true);

    osg::Vec3Array* vertexarray = new osg::Vec3Array;
    osg::Vec2Array* tcoords = new osg::Vec2Array;

    osg::Vec2 bottom_texcoord(0.0f, 0.0f);

    osg::Vec2 dx_texcoord(1.0f / (float)(NUM_ELEMENTS_X), 0.0f);
    osg::Vec2 dy_texcoord(0.0f, 1.0f / (float)(NUM_ELEMENTS_Y));



    for (int i = 0; i < NUM_ELEMENTS_X; i++)
    {
        osg::Vec2 texcoord = bottom_texcoord + dy_texcoord*(float)i;

        for (int j = 0; j < NUM_ELEMENTS_Y; j++)
        {
            vertexarray->push_back(osg::Vec3(texcoord.x(), texcoord.y(), 0.0));
            tcoords->push_back(osg::Vec2(texcoord.x(), texcoord.y()));
            texcoord += dx_texcoord;
        }
    }

    particleGeometry->setVertexArray(vertexarray);
    particleGeometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, numVertices));
    particleGeometry->setTexCoordArray(0, tcoords);
    //this glMemoryBarrier thing... not sure if we could better do instanced drawing?  all the data is in Shader Storage Buffer..
    particleGeometry->setVertexAttribArray(1, particleGeometry->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);

    _computationResultsRenderGroup->addChild(particleGeode);
    particleGeode->addDrawable(particleGeometry);

    addChild(_computationResultsRenderGroup.get());

}
Пример #3
0
osg::Node* CreateScene()
{
	osg::Group* pGroup = new osg::Group;

	pGroup->addChild( createDebugText() );

	//////////////////////////
	// simple terrain part I
	//////////////////////////

	osg::ref_ptr<CTerrain>	rTerrain = new CTerrain;
	rTerrain->Init();
	pGroup->addChild( rTerrain->getNodeWithShader() );

	//////////////////////////
	// RTT terrain
	//////////////////////////

	osg::Group* pRenderMe = new osg::Group;

	osg::Geode* pGeodeShape = new osg::Geode;
	pGeodeShape->addDrawable( new osg::ShapeDrawable( new osg::Sphere(osg::Vec3(0.0f,0.0f,4.0f),1.0f) ) );
	pGroup->addChild( pGeodeShape );
	pRenderMe->addChild( rTerrain->getNode() );
	pGroup->addChild( pGeodeShape );


	pHeightRTT = new CHeightRTT;
	pHeightRTT->init( rViewer, rTextCam, rViewer->getCamera(), pGroup, pRenderMe );

	//////////////////////////
	// simple terrain part II
	//////////////////////////

	rTerrain->setCam( pHeightRTT->getCamera() );
	
	//////////////////////////
	// grass billboard stuff
	//////////////////////////

	pGeodeGrass = new osg::Geode();
	
	geomGrass = createGrassGeom();
	pGeodeGrass->addDrawable( geomGrass );

	osg::ref_ptr< osg::Shader > vertexShader = new osg::Shader();
    vertexShader->setType( osg::Shader::VERTEX );
    vertexShader->loadShaderSourceFromFile( "grass.vert" );

	osg::ref_ptr< osg::Shader > fragShader = new osg::Shader();
    fragShader->setType( osg::Shader::FRAGMENT );
    fragShader->loadShaderSourceFromFile( "grass.frag" );

	osg::ref_ptr< osg::Program > program = new osg::Program();
    program->addShader( vertexShader.get() );
	program->addShader( fragShader.get() );

	ss = pGeodeGrass->getOrCreateStateSet();
	ss->setAttribute( program.get(), osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );

    uniformCC = new osg::Uniform("cc",(int)cc);
    ss->addUniform( uniformCC );
	
	// camera lock
	uniformLock = new osg::Uniform( "bLock", false );
    ss->addUniform( uniformLock );

	// spacing
	uniformSpacing = new osg::Uniform( "spacing", (float)sp );
    ss->addUniform( uniformSpacing );

	// height map
	ss->setTextureAttributeAndModes( 1, pHeightRTT->getTexture(), osg::StateAttribute::ON );
	osg::Uniform* uniformHeightMap = new osg::Uniform( "texHeightMap", 1 );
    ss->addUniform( uniformHeightMap );

	// height adjust
	uniformHeightAdjust = new osg::Uniform( "heightAdjust", heightAdjust );
    ss->addUniform( uniformHeightAdjust );

	// wind factor
	uniformWindFactor = new osg::Uniform( "windFactor", windFactor );
    ss->addUniform( uniformWindFactor );

	// grass stretch
	uniformGrassStretch = new osg::Uniform( "grassStretch", grassStretch );
    ss->addUniform( uniformGrassStretch );

	// ortho camera inverse view matrix
	osg::Uniform* uniformOrthoInverseViewMatrix = new osg::Uniform( "orthoInverseViewMatrix", pHeightRTT->getCamera()->getInverseViewMatrix() );
    ss->addUniform( uniformOrthoInverseViewMatrix );
	pGeodeGrass->setUpdateCallback( new CMyUpdateCallbackGrass( pHeightRTT->getCamera(), uniformOrthoInverseViewMatrix ) );

	osg::Texture2D* pTex = new osg::Texture2D;
    osg::Image* pImage = osgDB::readImageFile( "grass2.tga" );
    pTex->setImage( pImage );
    ss->setTextureAttributeAndModes( 0, pTex, osg::StateAttribute::ON );
	ss->setMode( GL_BLEND, osg::StateAttribute::ON );
	ss->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
	ss->setRenderBinDetails( 9, "DepthSortedBin" );

	ss->setMode( GL_ALPHA_TEST, osg::StateAttribute::ON );
	ss->setAttribute( new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.9f), osg::StateAttribute::ON );

	pGroup->addChild( pGeodeGrass );

	////////////////
	// a grid
	////////////////
	CSulGeomGrid* pGeomGrid = new CSulGeomGrid;
    pGeomGrid->Create( osg::Vec3(0,0,0), 10, 10, 1, 1, 5, 5 );
	pGroup->addChild( pGeomGrid );

	// what does this exactly do? (you would think it would disable culling,.. but it doesn't!)
	// think it culls when an object is a certain pixel size on the screen
	pGroup->setCullingActive( false );

	return pGroup;
}
Пример #4
0
bool
ShaderGenerator::processGeometry( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& replacement )
{
    // do nothing if there's no GLSL support
    if ( !Registry::capabilities().supportsGLSL() )
        return false;

    // State object with extra accessors:
    StateEx* state = static_cast<StateEx*>(_state.get());

    // check for a real osg::Program in the whole state stack. If it exists, bail out
    // so that OSG can use the program already in the graph. We never override a
    // full Program.
    osg::StateAttribute* program = state->getAttribute(osg::StateAttribute::PROGRAM);
    if ( dynamic_cast<osg::Program*>(program) != 0L )
        return false;

    // see if the current state set contains a VirtualProgram already. If so,
    // we will add to it if necessary.
    osg::ref_ptr<VirtualProgram> vp = 
        dynamic_cast<VirtualProgram*>( ss->getAttribute(VirtualProgram::SA_TYPE) );

    // Check whether the lighting state has changed and install a mode uniform.
    if ( ss->getMode(GL_LIGHTING) != osg::StateAttribute::INHERIT )
    {
        if ( !replacement.valid() )
            replacement = osg::clone(ss, osg::CopyOp::SHALLOW_COPY);
        //if ( !replacement.valid() ) 
        //    replacement = osg::clone(ss, osg::CopyOp::DEEP_COPY_ALL);

        osg::StateAttribute::GLModeValue value = state->getMode(GL_LIGHTING); // from the state, not the ss.
        replacement->addUniform( Registry::shaderFactory()->createUniformForGLMode(GL_LIGHTING, value) );
    }

    // if the stateset changes any texture attributes, we need a new virtual program:
    if (ss->getTextureAttributeList().size() > 0)
    {
        if ( !replacement.valid() )
            replacement = osg::clone(ss, osg::CopyOp::SHALLOW_COPY);
        //if ( !replacement.valid() ) 
        //    replacement = osg::clone(ss, osg::CopyOp::DEEP_COPY_ALL);

        // work off the state's accumulated texture attribute set:
        int texCount = state->getNumTextureAttributes();

        if ( !vp )
        {
            vp = osg::clone( _defaultVP.get() );
            replacement->setAttributeAndModes( vp, osg::StateAttribute::ON );
        }

        // start generating the shader source.
        std::stringstream vertHead, vertBody, fragHead, fragBody;

        // compatibility strings make it work in GL or GLES.
        vertHead << "#version " GLSL_VERSION_STR "\n" GLSL_PRECISION;
        fragHead << "#version " GLSL_VERSION_STR "\n" GLSL_PRECISION;

        // function declarations:
        vertBody << "void " VERTEX_FUNCTION "(inout vec4 vertex_view)\n{\n";

        fragBody << "void " FRAGMENT_FUNCTION "(inout vec4 color)\n{\n";

        for( int t = 0; t < texCount; ++t )
        {
            if (t == 0)
            {
                fragBody << INDENT << MEDIUMP "vec4 texel; \n";
            }

            osg::StateAttribute* tex = state->getTextureAttribute( t, osg::StateAttribute::TEXTURE );
            if ( tex )
            {
                // see if we have a texenv; if so get its blending mode.
                osg::TexEnv::Mode blendingMode = osg::TexEnv::MODULATE;
                osg::TexEnv* env = dynamic_cast<osg::TexEnv*>(state->getTextureAttribute(t, osg::StateAttribute::TEXENV) );
                if ( env )
                {
                    blendingMode = env->getMode();
                    if ( blendingMode == osg::TexEnv::BLEND )
                    {
                        replacement->getOrCreateUniform( Stringify() << TEXENV_COLOR << t, osg::Uniform::FLOAT_VEC4 )->set( env->getColor() );
                    }
                }

                osg::TexGen::Mode texGenMode = osg::TexGen::OBJECT_LINEAR;
                osg::TexGen* texGen = dynamic_cast<osg::TexGen*>(state->getTextureAttribute(t, osg::StateAttribute::TEXGEN));
                if ( texGen )
                {
                    texGenMode = texGen->getMode();
                }

                vertHead << "varying " MEDIUMP "vec4 " TEX_COORD << t << ";\n";
                fragHead << "varying " MEDIUMP "vec4 " TEX_COORD << t << ";\n";

                // handle different TexGen modes.
                switch(texGenMode)
                {
                case osg::TexGen::SPHERE_MAP:
                    vertBody 
                        //todo: consolidate.
                        << INDENT "{\n" // scope it in case there are > 1
                        << INDENT "vec3 v = normalize(vec3(vertex_view));\n"
                        << INDENT "vec3 n = normalize(gl_NormalMatrix * gl_Normal);\n"
                        << INDENT "vec3 r = reflect(v, n);\n"
                        << INDENT "float m = 2.0 * sqrt(r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0));\n"
                        << INDENT TEX_COORD << t << ".s = r.x/m + 0.5;\n"
                        << INDENT TEX_COORD << t << ".t = r.y/m + 0.5;\n"
                        << INDENT "}\n";
                    break;

                default:
                    vertBody 
                        << INDENT << TEX_COORD << t << " = gl_MultiTexCoord" << t << ";\n";
                    break;
                }


                if ( dynamic_cast<osg::Texture1D*>(tex) )
                {
                    fragHead << "uniform sampler1D " SAMPLER << t << ";\n";
                    fragBody << INDENT "texel = texture1D(" SAMPLER << t << ", " TEX_COORD << t << ".x);\n";
                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_1D )->set( t );
                }
#if 1
                else if ( dynamic_cast<osg::Texture2D*>(tex) )
                {
                    fragHead << "uniform sampler2D " SAMPLER << t << ";\n";
                    fragBody << INDENT "texel = texture2D(" SAMPLER << t << ", " TEX_COORD << t << ".xy);\n";
                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_2D )->set( t );
                }

#else // embosser
                else if ( dynamic_cast<osg::Texture2D*>(tex) )
                {
                    fragHead << "uniform sampler2D " SAMPLER << t << ";\n";
                    fragBody 
                        << INDENT "{\n"
                        << INDENT "float bs = 1.0/256.0;\n"
                        << INDENT "vec4 bm = vec4(0.0);\n"
                        << INDENT "float u = " TEX_COORD << t << ".x;\n"
                        << INDENT "float v = " TEX_COORD << t << ".y;\n"
                        << INDENT "texel = texture2D(" SAMPLER << t << ", vec2(u, v)); \n"
                        << INDENT "bm = texture2D(" SAMPLER << t << ", vec2(u-bs, v-bs)) + \n"
                        << INDENT "     texture2D(" SAMPLER << t << ", vec2(u-bs, v-bs)) - \n"
                        << INDENT "     texel                                            - \n"
                        << INDENT "     texture2D(" SAMPLER << t << ", vec2(u+bs, v+bs));  \n"
                        << INDENT "texel *= vec4( 2.0*(bm.rgb+vec3(0.5,0.5,0.5)),1.0 );\n"
                        << INDENT "}\n";

                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_2D )->set( t );
                }
#endif

#if 0 // works, but requires a higher version of GL?
                else if ( dynamic_cast<osg::TextureRectangle*>(tex) )
                {
                    fragHead << "uniform sampler2Drect " SAMPLER << t << ";\n";
                    fragBody << INDENT "texel = texture2Drect(" SAMPLER << t << ", " TEX_COORD << t << ".xy);\n";
                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_2D_RECT )->set( t );
                }
#endif
                // doesn't work. why?
                else if ( dynamic_cast<osg::TextureRectangle*>(tex) )
                {
                    osg::Image* image = static_cast<osg::TextureRectangle*>(tex)->getImage();

                    vertBody 
                        << INDENT << TEX_COORD << t << ".x /= " << (image->s()-1) << ".0;\n"
                        << INDENT << TEX_COORD << t << ".y /= " << (image->t()-1) << ".0;\n";

                    fragHead << "uniform sampler2D " SAMPLER << t << ";\n";
                    fragBody << INDENT "texel = texture2D(" SAMPLER << t << ", " TEX_COORD << t << ".xy);\n";
                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_2D )->set( t );
                }

                else if ( dynamic_cast<osg::Texture3D*>(tex) )
                {
                    fragHead << "uniform sampler3D " SAMPLER << t << ";\n";
                    fragBody << INDENT "texel = texture3D(" SAMPLER << t << ", " TEX_COORD << t << ".xyz);\n";
                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_3D )->set( t );
                }

                // See http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml
                switch( blendingMode )
                {
                case osg::TexEnv::REPLACE:
                    fragBody
                        << INDENT "color = texel; \n";
                    break;
                case osg::TexEnv::MODULATE:
                    fragBody
                        << INDENT "color = color * texel; \n";
                    break;
                case osg::TexEnv::DECAL:
                    fragBody
                        << INDENT "color.rgb = color.rgb * (1.0 - texel.a) + (texel.rgb * texel.a); \n";
                    break;
                case osg::TexEnv::BLEND:
                    fragHead
                        << "uniform " MEDIUMP "vec4 " TEXENV_COLOR << t << "\n;";
                    fragBody
                        << INDENT "color.rgb = color.rgb * (1.0 - texel.rgb) + (" << TEXENV_COLOR << t << ".rgb * texel.rgb); \n"
                        << INDENT "color.a   = color.a * texel.a; \n";
                    break;
                case osg::TexEnv::ADD:
                default:
                    fragBody
                        << INDENT "color.rgb = color.rgb + texel.rgb; \n"
                        << INDENT "color.a   = color.a * texel.a; \n";
                }
            }
        }

        // close out functions:
        vertBody << "}\n";
        fragBody << "}\n";

        // Extract the shader source strings (win compat method)
        std::string vertBodySrc, vertSrc, fragBodySrc, fragSrc;
        vertBodySrc = vertBody.str();
        vertHead << vertBodySrc;
        vertSrc = vertHead.str();
        fragBodySrc = fragBody.str();
        fragHead << fragBodySrc;
        fragSrc = fragHead.str();

        // inject the shaders:
        vp->setFunction( VERTEX_FUNCTION,   vertSrc, ShaderComp::LOCATION_VERTEX_VIEW );
        vp->setFunction( FRAGMENT_FUNCTION, fragSrc, ShaderComp::LOCATION_FRAGMENT_COLORING );
    }

    return replacement.valid();
}
Пример #5
0
bool
ShaderGenerator::processGeometry( osg::StateSet* ss, osg::ref_ptr<osg::StateSet>& replacement )
{
    // do nothing if there's no GLSL support
    if ( !Registry::capabilities().supportsGLSL() )
        return false;

    // State object with extra accessors:
    StateEx* state = static_cast<StateEx*>(_state.get());

    // check for a real osg::Program in the whole state stack. If it exists, bail out
    // so that OSG can use the program already in the graph. We never override a
    // full Program.
    osg::StateAttribute* program = state->getAttribute(osg::StateAttribute::PROGRAM);
    if ( dynamic_cast<osg::Program*>(program) != 0L )
        return false;

    // see if the current state set contains a VirtualProgram already. If so,
    // we will add to it if necessary.
    VirtualProgram* vp = dynamic_cast<VirtualProgram*>( ss->getAttribute(VirtualProgram::SA_TYPE) );

    // Check whether the lighting state has changed and install a mode uniform.
    if ( ss->getMode(GL_LIGHTING) != osg::StateAttribute::INHERIT )
    {
        if ( !replacement.valid() ) 
            replacement = osg::clone(ss, osg::CopyOp::DEEP_COPY_ALL);

        ShaderFactory* sf = Registry::instance()->getShaderFactory();
        osg::StateAttribute::GLModeValue value = state->getMode(GL_LIGHTING); // from the state, not the ss.
        replacement->addUniform( sf->createUniformForGLMode(GL_LIGHTING, value) );
    }

    // if the stateset changes any texture attributes, we need a new virtual program:
    if (ss->getTextureAttributeList().size() > 0)
    {
        if ( !replacement.valid() ) 
            replacement = osg::clone(ss, osg::CopyOp::DEEP_COPY_ALL);

        // work off the state's accumulated texture attribute set:
        int texCount = state->getNumTextureAttributes();

        if ( !vp )
        {
            vp = osg::clone( _defaultVP.get() );
            replacement->setAttributeAndModes( vp, osg::StateAttribute::ON );
        }

        // start generating the shader source.
        std::stringstream vertHead, vertBody, fragHead, fragBody;

        // compatibility strings make it work in GL or GLES.
        vertHead << "#version " GLSL_VERSION_STR "\n" GLSL_PRECISION;
        fragHead << "#version " GLSL_VERSION_STR "\n" GLSL_PRECISION;

        // function declarations:
        vertBody << "void " VERTEX_FUNCTION "()\n{\n";

        fragBody << "void " FRAGMENT_FUNCTION "(inout vec4 color)\n{\n";

        for( int t = 0; t < texCount; ++t )
        {
            if (t == 0)
            {
                fragBody << INDENT << MEDIUMP "vec4 texel; \n";
            }

            osg::StateAttribute* tex = state->getTextureAttribute( t, osg::StateAttribute::TEXTURE );
            if ( tex )
            {
                // see if we have a texenv; if so get its blending mode.
                osg::TexEnv::Mode blendingMode = osg::TexEnv::MODULATE;
                osg::TexEnv* env = dynamic_cast<osg::TexEnv*>(state->getTextureAttribute(t, osg::StateAttribute::TEXENV) );
                if ( env )
                {
                    blendingMode = env->getMode();
                    if ( blendingMode == osg::TexEnv::BLEND )
                    {
                        replacement->getOrCreateUniform( Stringify() << TEXENV_COLOR << t, osg::Uniform::FLOAT_VEC4 )->set( env->getColor() );
                    }
                }

                vertHead << "varying " MEDIUMP "vec4 " TEX_COORD << t << ";\n";
                vertBody << INDENT << TEX_COORD << t << " = gl_MultiTexCoord" << t << ";\n";

                fragHead << "varying " MEDIUMP "vec4 " TEX_COORD << t << ";\n";

                if ( dynamic_cast<osg::Texture1D*>(tex) )
                {
                    fragHead << "uniform sampler1D " SAMPLER << t << ";\n";
                    fragBody << INDENT "texel = texture1D(" SAMPLER << t << ", " TEX_COORD << t << ".x);\n";
                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_1D )->set( t );
                }
                else if ( dynamic_cast<osg::Texture2D*>(tex) )
                {
                    fragHead << "uniform sampler2D " SAMPLER << t << ";\n";
                    fragBody << INDENT "texel = texture2D(" SAMPLER << t << ", " TEX_COORD << t << ".xy);\n";
                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_2D )->set( t );
                }
                else if ( dynamic_cast<osg::Texture3D*>(tex) )
                {
                    fragHead << "uniform sampler3D " SAMPLER << t << ";\n";
                    fragBody << INDENT "texel = texture3D(" SAMPLER << t << ", " TEX_COORD << t << ".xyz);\n";
                    replacement->getOrCreateUniform( Stringify() << SAMPLER << t, osg::Uniform::SAMPLER_3D )->set( t );
                }

                // See http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml
                switch( blendingMode )
                {
                case osg::TexEnv::REPLACE:
                    fragBody
                        << INDENT "color = texel; \n";
                    break;
                case osg::TexEnv::MODULATE:
                    fragBody
                        << INDENT "color = color * texel; \n";
                    break;
                case osg::TexEnv::DECAL:
                    fragBody
                        << INDENT "color.rgb = color.rgb * (1.0 - texel.a) + (texel.rgb * texel.a); \n";
                    break;
                case osg::TexEnv::BLEND:
                    fragHead
                        << "uniform " MEDIUMP "vec4 " TEXENV_COLOR << t << "\n;";
                    fragBody
                        << INDENT "color.rgb = color.rgb * (1.0 - texel.rgb) + (" << TEXENV_COLOR << t << ".rgb * texel.rgb); \n"
                        << INDENT "color.a   = color.a * texel.a; \n";
                    break;
                case osg::TexEnv::ADD:
                default:
                    fragBody
                        << INDENT "color.rgb = color.rgb + texel.rgb; \n"
                        << INDENT "color.a   = color.a * texel.a; \n";
                }
            }
        }

        // close out functions:
        vertBody << "}\n";
        fragBody << "}\n";

        // Extract the shader source strings (win compat method)
        std::string vertBodySrc, vertSrc, fragBodySrc, fragSrc;
        vertBodySrc = vertBody.str();
        vertHead << vertBodySrc;
        vertSrc = vertHead.str();
        fragBodySrc = fragBody.str();
        fragHead << fragBodySrc;
        fragSrc = fragHead.str();

        // inject the shaders:
        vp->setFunction( VERTEX_FUNCTION,   vertSrc, ShaderComp::LOCATION_VERTEX_PRE_LIGHTING );
        vp->setFunction( FRAGMENT_FUNCTION, fragSrc, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
    }

    return replacement.valid();
}