From Maciej Krol, "As promised to Roland I assembled simple shader generator. ShaderGenVisitor converts accumulated fixed function pipeline state sets to ones with shader programs. Generated state sets are attached to geometries and stored in ShaderGenCache for reuse.

Very simple cases of state configuration are supported (all the ones I really need):
- single per pixel not attenuated non spot light source ON/OFF
- exp2 fog ON/OFF
- diffuse texture in rgb + optional specular gloss in alpha (Texture unit 0) ON/OFF
- normal map texture (Texture unit 1 and Tangent in VertexAttribArray 6) ON/OFF
- blending and alpha testing (not in shader pipeline)

To view fixed function pipeline files and paged databases simply run >osgshadergen myfile.osg"
This commit is contained in:
Robert Osfield 2009-03-23 11:53:06 +00:00
parent ffbed2b222
commit 1fd5eefbcf
6 changed files with 643 additions and 0 deletions

View File

@ -85,6 +85,7 @@ IF(DYNAMIC_OPENSCENEGRAPH)
ADD_SUBDIRECTORY(osgscribe)
ADD_SUBDIRECTORY(osgsequence)
ADD_SUBDIRECTORY(osgshaders)
ADD_SUBDIRECTORY(osgshadergen)
ADD_SUBDIRECTORY(osgshaderterrain)
ADD_SUBDIRECTORY(osgshadow)
ADD_SUBDIRECTORY(osgshape)

View File

@ -0,0 +1,6 @@
#this file is automatically generated
SET(TARGET_SRC osgshadergen.cpp )
#### end var setup ###
SETUP_EXAMPLE(osgshadergen)

View File

@ -0,0 +1,167 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commericial and non commericial applications,
* as long as this copyright notice is maintained.
*
* This application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <osgDB/ReadFile>
#include <osgUtil/ShaderGen>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgGA/StateSetManipulator>
#include <osgGA/AnimationPathManipulator>
#include <osgGA/TerrainManipulator>
#include <iostream>
class ShaderGenReadFileCallback : public osgDB::Registry::ReadFileCallback
{
public:
ShaderGenReadFileCallback()
{
}
virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& filename, const osgDB::ReaderWriter::Options* options)
{
osgDB::ReaderWriter::ReadResult result = osgDB::Registry::ReadFileCallback::readNode(filename, options);
if (osg::Node *node = result.getNode())
{
_visitor.reset();
node->accept(_visitor);
}
return result;
}
void setRootStateSet(osg::StateSet *stateSet) { _visitor.setRootStateSet(stateSet); }
osg::StateSet *getRootStateSet() const { return _visitor.getRootStateSet(); }
protected:
osgUtil::ShaderGenVisitor _visitor;
};
int main(int argc, char** argv)
{
// use an ArgumentParser object to manage the program arguments.
osg::ArgumentParser arguments(&argc,argv);
arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+
" is an example of conversion of fixed function pipeline to GLSL");
arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
osgViewer::Viewer viewer(arguments);
unsigned int helpType = 0;
if ((helpType = arguments.readHelpType()))
{
arguments.getApplicationUsage()->write(std::cout, helpType);
return 1;
}
// report any errors if they have occurred when parsing the program arguments.
if (arguments.errors())
{
arguments.writeErrorMessages(std::cout);
return 1;
}
if (arguments.argc()<=1)
{
arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
return 1;
}
// set up the camera manipulators.
{
osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() );
std::string pathfile;
char keyForAnimationPath = '5';
while (arguments.read("-p",pathfile))
{
osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile);
if (apm || !apm->valid())
{
unsigned int num = keyswitchManipulator->getNumMatrixManipulators();
keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm );
keyswitchManipulator->selectMatrixManipulator(num);
++keyForAnimationPath;
}
}
viewer.setCameraManipulator( keyswitchManipulator.get() );
}
// add the state manipulator
viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
// add the thread model handler
viewer.addEventHandler(new osgViewer::ThreadingHandler);
// add the window size toggle handler
viewer.addEventHandler(new osgViewer::WindowSizeHandler);
// add the stats handler
viewer.addEventHandler(new osgViewer::StatsHandler);
// add the help handler
viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
// add the record camera path handler
viewer.addEventHandler(new osgViewer::RecordCameraPathHandler);
// add the LOD Scale handler
viewer.addEventHandler(new osgViewer::LODScaleHandler);
// add the screen capture handler
viewer.addEventHandler(new osgViewer::ScreenCaptureHandler);
// Register shader generator callback
ShaderGenReadFileCallback *readFileCallback = new ShaderGenReadFileCallback;
// All read nodes will inherit root state set.
readFileCallback->setRootStateSet(viewer.getCamera()->getStateSet());
osgDB::Registry::instance()->setReadFileCallback(readFileCallback);
// load the data
osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
if (!loadedModel)
{
std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
return 1;
}
// any option left unread are converted into errors to write out later.
arguments.reportRemainingOptionsAsUnrecognized();
// report any errors if they have occurred when parsing the program arguments.
if (arguments.errors())
{
arguments.writeErrorMessages(std::cout);
return 1;
}
viewer.setSceneData( loadedModel.get() );
viewer.realize();
return viewer.run();
}

84
include/osgUtil/ShaderGen Normal file
View File

@ -0,0 +1,84 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
/**
* \brief Shader generator framework.
* \author Maciej Krol
*/
#ifndef OSGUTIL_SHADER_STATE_
#define OSGUTIL_SHADER_STATE_ 1
#include <osgUtil/Export>
#include <osg/NodeVisitor>
#include <osg/State>
namespace osgUtil
{
class OSGUTIL_EXPORT ShaderGenCache : public osg::Referenced
{
public:
enum StateMask
{
BLEND = 1,
LIGHTING = 2,
FOG = 4,
DIFFUSE_MAP = 8, //< Texture in unit 0
NORMAL_MAP = 16 //< Texture in unit 1 and vertex attribute array 6
};
typedef std::map<unsigned int, osg::ref_ptr<osg::StateSet> > StateSetMap;
ShaderGenCache() {};
void setStateSet(unsigned int stateMask, osg::StateSet *program);
osg::StateSet *getStateSet(unsigned int stateMask) const;
osg::StateSet *getOrCreateStateSet(unsigned int stateMask);
protected:
osg::StateSet *createStateSet(unsigned int stateMask) const;
mutable OpenThreads::Mutex _mutex;
StateSetMap _stateSetMap;
};
class OSGUTIL_EXPORT ShaderGenVisitor : public osg::NodeVisitor
{
public:
ShaderGenVisitor();
ShaderGenVisitor(ShaderGenCache *stateCache);
void setStateCache(ShaderGenCache *stateCache) { _stateCache = stateCache; }
ShaderGenCache *getStateCache() const { return _stateCache.get(); }
/// Top level state set applied as the first one.
void setRootStateSet(osg::StateSet *stateSet);
osg::StateSet *getRootStateSet() const { return _rootStateSet.get(); }
void apply(osg::Node &node);
void apply(osg::Geode &geode);
void reset();
protected:
void update(osg::Drawable *drawable);
osg::ref_ptr<ShaderGenCache> _stateCache;
osg::ref_ptr<osg::State> _state;
osg::ref_ptr<osg::StateSet> _rootStateSet;
};
}
#endif

View File

@ -37,6 +37,7 @@ SET(LIB_PUBLIC_HEADERS
${HEADER_PATH}/ReversePrimitiveFunctor
${HEADER_PATH}/SceneView
${HEADER_PATH}/SceneGraphBuilder
${HEADER_PATH}/ShaderGen
${HEADER_PATH}/Simplifier
${HEADER_PATH}/SmoothingVisitor
${HEADER_PATH}/StateGraph
@ -76,6 +77,7 @@ ADD_LIBRARY(${LIB_NAME}
RenderStage.cpp
ReversePrimitiveFunctor.cpp
SceneView.cpp
ShaderGen.cpp
Simplifier.cpp
SmoothingVisitor.cpp
SceneGraphBuilder.cpp

383
src/osgUtil/ShaderGen.cpp Normal file
View File

@ -0,0 +1,383 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
/**
* \brief Shader generator framework.
* \author Maciej Krol
*/
#include <osgUtil/ShaderGen>
#include <osg/Geode>
#include <osg/Geometry> // for ShaderGenVisitor::update
#include <sstream>
using namespace osgUtil;
namespace osgUtil
{
/// State extended by mode/attribute accessors
class StateEx : public osg::State
{
public:
StateEx() : State() {}
osg::StateAttribute::GLModeValue getMode(osg::StateAttribute::GLMode mode,
osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
{
return getMode(_modeMap, mode, def);
}
osg::StateAttribute *getAttribute(osg::StateAttribute::Type type, unsigned int member = 0) const
{
return getAttribute(_attributeMap, type, member);
}
osg::StateAttribute::GLModeValue getTextureMode(unsigned int unit,
osg::StateAttribute::GLMode mode,
osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
{
return unit < _textureModeMapList.size() ? getMode(_textureModeMapList[unit], mode, def) : def;
}
osg::StateAttribute *getTextureAttribute(unsigned int unit, osg::StateAttribute::Type type) const
{
return unit < _textureAttributeMapList.size() ? getAttribute(_textureAttributeMapList[unit], type, 0) : 0;
}
osg::Uniform *getUniform(const std::string& name) const
{
UniformMap::const_iterator it = _uniformMap.find(name);
return it != _uniformMap.end() ?
const_cast<osg::Uniform *>(it->second.uniformVec.back().first) : 0;
}
protected:
osg::StateAttribute::GLModeValue getMode(const ModeMap &modeMap,
osg::StateAttribute::GLMode mode,
osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
{
ModeMap::const_iterator it = modeMap.find(mode);
return (it != modeMap.end() && it->second.valueVec.size()) ? it->second.valueVec.back() : def;
}
osg::StateAttribute *getAttribute(const AttributeMap &attributeMap,
osg::StateAttribute::Type type, unsigned int member = 0) const
{
AttributeMap::const_iterator it = attributeMap.find(std::make_pair(type, member));
return (it != attributeMap.end() && it->second.attributeVec.size()) ?
const_cast<osg::StateAttribute*>(it->second.attributeVec.back().first) : 0;
}
};
}
void ShaderGenCache::setStateSet(unsigned int stateMask, osg::StateSet *stateSet)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
_stateSetMap[stateMask] = stateSet;
}
osg::StateSet *ShaderGenCache::getStateSet(unsigned int stateMask) const
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
StateSetMap::const_iterator it = _stateSetMap.find(stateMask);
return (it != _stateSetMap.end()) ? it->second.get() : 0;
}
osg::StateSet *ShaderGenCache::getOrCreateStateSet(unsigned int stateMask)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
StateSetMap::iterator it = _stateSetMap.find(stateMask);
if (it == _stateSetMap.end())
{
osg::StateSet *stateSet = createStateSet(stateMask);
_stateSetMap.insert(it, std::make_pair(stateMask, stateSet));
return stateSet;
}
return it->second.get();
}
osg::StateSet *ShaderGenCache::createStateSet(unsigned int stateMask) const
{
osg::StateSet *stateSet = new osg::StateSet;
osg::Program *program = new osg::Program;
stateSet->setAttribute(program);
std::ostringstream vert;
std::ostringstream frag;
// write varyings
if ((stateMask & LIGHTING) && !(stateMask & NORMAL_MAP))
{
vert << "varying vec3 normalDir;\n";
}
if (stateMask & (LIGHTING | NORMAL_MAP))
{
vert << "varying vec3 lightDir;\n";
}
if (stateMask & (LIGHTING | NORMAL_MAP | FOG))
{
vert << "varying vec3 viewDir;\n";
}
// copy varying to fragment shader
frag << vert.str();
// write uniforms and attributes
int unit = 0;
if (stateMask & DIFFUSE_MAP)
{
osg::Uniform *diffuseMap = new osg::Uniform("diffuseMap", unit++);
stateSet->addUniform(diffuseMap);
frag << "uniform sampler2D diffuseMap;\n";
}
if (stateMask & NORMAL_MAP)
{
osg::Uniform *normalMap = new osg::Uniform("normalMap", unit++);
stateSet->addUniform(normalMap);
frag << "uniform sampler2D normalMap;\n";
program->addBindAttribLocation("tangent", 6);
vert << "attribute vec3 tangent;\n";
}
vert << "\n"\
"void main()\n"\
"{\n"\
" gl_Position = ftransform();\n";
if (stateMask & (DIFFUSE_MAP | NORMAL_MAP))
{
vert << " gl_TexCoord[0] = gl_MultiTexCoord0;\n";
}
if (stateMask & NORMAL_MAP)
{
vert <<
" vec3 n = gl_NormalMatrix * gl_Normal;\n"\
" vec3 t = gl_NormalMatrix * tangent;\n"\
" vec3 b = cross(n, t);\n"\
" vec3 dir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
" viewDir.x = dot(dir, t);\n"\
" viewDir.y = dot(dir, b);\n"\
" viewDir.z = dot(dir, n);\n"\
" vec4 lpos = gl_LightSource[0].position;\n"\
" if (lpos.w == 0.0)\n"\
" dir = lpos.xyz;\n"\
" else\n"\
" dir += lpos.xyz;\n"\
" lightDir.x = dot(dir, t);\n"\
" lightDir.y = dot(dir, b);\n"\
" lightDir.z = dot(dir, n);\n";
}
else if (stateMask & LIGHTING)
{
vert <<
" normalDir = gl_NormalMatrix * gl_Normal;\n"\
" vec3 dir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
" viewDir = dir;\n"\
" vec4 lpos = gl_LightSource[0].position;\n"\
" if (lpos.w == 0.0)\n"\
" lightDir = lpos.xyz;\n"\
" else\n"\
" lightDir = lpos.xyz + dir;\n";
}
else if (stateMask & FOG)
{
vert <<
" viewDir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
" gl_FrontColor = gl_Color;\n";
}
else
{
vert << " gl_FrontColor = gl_Color;\n";
}
vert << "}\n";
frag << "\n"\
"void main()\n"\
"{\n";
if (stateMask & DIFFUSE_MAP)
{
frag << " vec4 base = texture2D(diffuseMap, gl_TexCoord[0].st);\n";
}
else
{
frag << " vec4 base = vec4(1.0);\n";
}
if (stateMask & NORMAL_MAP)
{
frag << " vec3 normalDir = texture2D(normalMap, gl_TexCoord[0].st).xyz*2.0-1.0;\n";
}
if (stateMask & (LIGHTING | NORMAL_MAP))
{
frag <<
" vec3 nd = normalize(normalDir);\n"\
" vec3 ld = normalize(lightDir);\n"\
" vec3 vd = normalize(viewDir);\n"\
" vec4 color = gl_FrontLightModelProduct.sceneColor;\n"\
" color += gl_FrontLightProduct[0].ambient;\n"\
" float diff = max(dot(ld, nd), 0.0);\n"\
" color += gl_FrontLightProduct[0].diffuse * diff;\n"\
" color *= base;\n"\
" if (diff > 0.0)\n"\
" {\n"\
" vec3 halfDir = normalize(ld+vd);\n"\
" color.rgb += base.a * gl_FrontLightProduct[0].specular.rgb * \n"\
" pow(max(dot(halfDir, nd), 0.0), gl_FrontMaterial.shininess);\n"\
" }\n";
}
else
{
frag << " vec4 color = base;\n";
}
if (!(stateMask & LIGHTING))
{
frag << " color *= gl_Color;\n";
}
if (stateMask & FOG)
{
frag <<
" float d2 = dot(viewDir, viewDir);//gl_FragCoord.z/gl_FragCoord.w;\n"\
" float f = exp2(-1.442695*gl_Fog.density*gl_Fog.density*d2);\n"\
" color.rgb = mix(gl_Fog.color.rgb, color.rgb, clamp(f, 0.0, 1.0));\n";
}
frag << " gl_FragColor = color;\n";
frag << "}\n";
std::string vertstr = vert.str();
std::string fragstr = frag.str();
osg::notify(osg::DEBUG_INFO) << "ShaderGenCache Vertex shader:\n" << vertstr << std::endl;
osg::notify(osg::DEBUG_INFO) << "ShaderGenCache Fragment shader:\n" << fragstr << std::endl;
program->addShader(new osg::Shader(osg::Shader::VERTEX, vertstr));
program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragstr));
return stateSet;
}
ShaderGenVisitor::ShaderGenVisitor() :
NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_stateCache(new ShaderGenCache),
_state(new StateEx)
{
}
ShaderGenVisitor::ShaderGenVisitor(ShaderGenCache *stateCache) :
NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_stateCache(stateCache),
_state(new StateEx)
{
}
void ShaderGenVisitor::setRootStateSet(osg::StateSet *stateSet)
{
if (_rootStateSet.valid())
_state->removeStateSet(0);
_rootStateSet = stateSet;
if (_rootStateSet.valid())
_state->pushStateSet(_rootStateSet.get());
}
void ShaderGenVisitor::reset()
{
_state->popAllStateSets();
if (_rootStateSet.valid())
_state->pushStateSet(_rootStateSet.get());
}
void ShaderGenVisitor::apply(osg::Node &node)
{
osg::StateSet *stateSet = node.getStateSet();
if (stateSet)
_state->pushStateSet(stateSet);
traverse(node);
if (stateSet)
_state->popStateSet();
}
void ShaderGenVisitor::apply(osg::Geode &geode)
{
osg::StateSet *stateSet = geode.getStateSet();
if (stateSet)
_state->pushStateSet(stateSet);
for (unsigned int i=0; i<geode.getNumDrawables(); ++i)
{
osg::Drawable *drawable = geode.getDrawable(i);
osg::StateSet *ss = drawable->getStateSet();
if (ss)
_state->pushStateSet(ss);
update(drawable);
if (ss)
_state->popStateSet();
}
if (stateSet)
_state->popStateSet();
}
void ShaderGenVisitor::update(osg::Drawable *drawable)
{
// update only geometry due to compatibility issues with user defined drawables
osg::Geometry *geometry = drawable->asGeometry();
if (!geometry)
return;
StateEx *state = static_cast<StateEx *>(_state.get());
// skip nodes without state sets
if (state->getStateSetStackSize() == (_rootStateSet.valid() ? 1 : 0))
return;
// skip state sets with already attached programs
if (state->getAttribute(osg::StateAttribute::PROGRAM))
return;
unsigned int stateMask = 0;
//if (state->getMode(GL_BLEND) & osg::StateAttribute::ON)
// stateMask |= ShaderGen::BLEND;
if (state->getMode(GL_LIGHTING) & osg::StateAttribute::ON)
stateMask |= ShaderGenCache::LIGHTING;
if (state->getMode(GL_FOG) & osg::StateAttribute::ON)
stateMask |= ShaderGenCache::FOG;
if (state->getTextureAttribute(0, osg::StateAttribute::TEXTURE))
stateMask |= ShaderGenCache::DIFFUSE_MAP;
if (state->getTextureAttribute(1, osg::StateAttribute::TEXTURE) &&
geometry->getVertexAttribArray(6)) //tangent
stateMask |= ShaderGenCache::NORMAL_MAP;
// Get program and uniforms for accumulated state.
osg::StateSet *progss = _stateCache->getOrCreateStateSet(stateMask);
// Set program and uniforms to the last state set.
osg::StateSet *ss = const_cast<osg::StateSet *>(state->getStateSetStack().back());
ss->setAttribute(progss->getAttribute(osg::StateAttribute::PROGRAM));
ss->setUniformList(progss->getUniformList());
}