Refactored osgUtil::ShaderGen to use #pragma(tic) shader composition.

This commit is contained in:
Robert Osfield 2018-03-14 08:22:45 +00:00
parent 009580676f
commit 4447190dd6
5 changed files with 164 additions and 418 deletions

View File

@ -10,6 +10,7 @@
*/ */
#include <osgDB/ReadFile> #include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/ShaderGen> #include <osgUtil/ShaderGen>
#include <osgViewer/Viewer> #include <osgViewer/Viewer>
@ -26,31 +27,6 @@
#include <iostream> #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) int main(int argc, char** argv)
{ {
@ -64,6 +40,8 @@ int main(int argc, char** argv)
osgViewer::Viewer viewer(arguments); osgViewer::Viewer viewer(arguments);
unsigned int helpType = 0; unsigned int helpType = 0;
if ((helpType = arguments.readHelpType())) if ((helpType = arguments.readHelpType()))
{ {
@ -134,11 +112,10 @@ int main(int argc, char** argv)
// add the screen capture handler // add the screen capture handler
viewer.addEventHandler(new osgViewer::ScreenCaptureHandler); viewer.addEventHandler(new osgViewer::ScreenCaptureHandler);
// Register shader generator callback
ShaderGenReadFileCallback *readFileCallback = new ShaderGenReadFileCallback;
// All read nodes will inherit root state set. std::string outputFilename;
readFileCallback->setRootStateSet(viewer.getCamera()->getStateSet()); if (arguments.read("-o", outputFilename)) {}
osgDB::Registry::instance()->setReadFileCallback(readFileCallback);
// load the data // load the data
osg::ref_ptr<osg::Node> loadedModel = osgDB::readRefNodeFiles(arguments); osg::ref_ptr<osg::Node> loadedModel = osgDB::readRefNodeFiles(arguments);
@ -148,6 +125,20 @@ int main(int argc, char** argv)
return 1; return 1;
} }
// run the shadergen on the loaded scene graph, and assign the uber shader
osgUtil::ShaderGenVisitor shadergen;
shadergen.assignUberProgram(viewer.getCamera()->getStateSet());
//shadergen.assignUberProgram(loadedModel->getOrCreateStateSet());
loadedModel->accept(shadergen);
if (!outputFilename.empty())
{
osgDB::writeNodeFile(*loadedModel, outputFilename);
osgDB::writeObjectFile(*(viewer.getCamera()->getStateSet()),"rootStateSet.osgt");
return 0;
}
// any option left unread are converted into errors to write out later. // any option left unread are converted into errors to write out later.
arguments.reportRemainingOptionsAsUnrecognized(); arguments.reportRemainingOptionsAsUnrecognized();

View File

@ -26,57 +26,19 @@
namespace osgUtil 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<int, osg::ref_ptr<osg::StateSet> > StateSetMap;
ShaderGenCache() {};
void setStateSet(int stateMask, osg::StateSet *program);
osg::StateSet *getStateSet(int stateMask) const;
osg::StateSet *getOrCreateStateSet(int stateMask);
protected:
osg::StateSet *createStateSet(int stateMask) const;
mutable OpenThreads::Mutex _mutex;
StateSetMap _stateSetMap;
};
class OSGUTIL_EXPORT ShaderGenVisitor : public osg::NodeVisitor class OSGUTIL_EXPORT ShaderGenVisitor : public osg::NodeVisitor
{ {
public: public:
ShaderGenVisitor(); ShaderGenVisitor();
ShaderGenVisitor(ShaderGenCache *stateCache);
void setStateCache(ShaderGenCache *stateCache) { _stateCache = stateCache; } /// assign default uber program to specified StateSet - typically the root node of the scene graph or the view's Camera
ShaderGenCache *getStateCache() const { return _stateCache.get(); } void assignUberProgram(osg::StateSet *stateSet);
/// Top level state set applied as the first one. void apply(osg::Node& node);
void setRootStateSet(osg::StateSet *stateSet);
osg::StateSet *getRootStateSet() const { return _rootStateSet.get(); }
void apply(osg::Node &node); void remapStateSet(osg::StateSet* stateSet);
void apply(osg::Drawable &drawable);
void reset();
protected: protected:
void update(osg::Drawable *drawable);
osg::ref_ptr<ShaderGenCache> _stateCache;
osg::ref_ptr<osg::State> _state;
osg::ref_ptr<osg::StateSet> _rootStateSet;
}; };
} }

View File

@ -22,379 +22,93 @@
#include <osg/Fog> #include <osg/Fog>
#include <sstream> #include <sstream>
#include "shaders/shadergen_vert.cpp"
#include "shaders/shadergen_frag.cpp"
using namespace osgUtil; using namespace osgUtil;
namespace osgUtil namespace osgUtil
{ {
/// State extended by mode/attribute accessors osg::ref_ptr<osg::Program> s_UberProgram;
class StateEx : public osg::State
struct UberProgramConstructor
{ {
public: UberProgramConstructor()
StateEx() : State() {}
osg::StateAttribute::GLModeValue getMode(osg::StateAttribute::GLMode mode,
osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
{ {
return getMode(_modeMap, mode, def); s_UberProgram = new osg::Program;
} s_UberProgram->addShader(new osg::Shader(osg::Shader::VERTEX, shadergen_vert));
s_UberProgram->addShader(new osg::Shader(osg::Shader::FRAGMENT, shadergen_frag));
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;
} }
}; };
} UberProgramConstructor s_UberProgramConstructor;
void ShaderGenCache::setStateSet(int stateMask, osg::StateSet *stateSet)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
_stateSetMap[stateMask] = stateSet;
}
osg::StateSet *ShaderGenCache::getStateSet(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(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, StateSetMap::value_type(stateMask, stateSet));
return stateSet;
}
return it->second.get();
}
osg::StateSet *ShaderGenCache::createStateSet(int stateMask) const
{
osg::StateSet *stateSet = new osg::StateSet;
osg::Program *program = new osg::Program;
stateSet->setAttribute(program);
std::ostringstream vert_frag;
vert_frag << "// ShaderGen shader\n";
vert_frag << "#ifdef GL_ES\n"
" precision highp float;\n"
"#endif\n";
// write varyings
if ((stateMask & LIGHTING) && !(stateMask & NORMAL_MAP))
{
vert_frag << "varying vec3 normalDir;\n";
}
if (stateMask & (LIGHTING | NORMAL_MAP))
{
vert_frag << "varying vec3 lightDir;\n";
}
if (stateMask & (LIGHTING | NORMAL_MAP | FOG))
{
vert_frag << "varying vec3 viewDir;\n";
}
vert_frag << "varying vec4 vertexColor;\n";
std::ostringstream vert;
std::ostringstream frag;
// copy varying to vertex ad fragment shader
vert << vert_frag.str();
frag << vert_frag.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";
}
#if !OSG_GLES2_FEATURES && !OSG_GLES3_FEATURES && !OSG_GL2_FEATURES
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"\
" vertexColor = gl_Color;\n";
}
#endif
else
{
vert << " vertexColor = gl_Color;\n";
}
vert << "}\n";
frag << "\n"\
"void main()\n"\
"{\n";
if (stateMask & DIFFUSE_MAP)
{
frag << " vec4 base = vertexColor * texture2D(diffuseMap, gl_TexCoord[0].st);\n";
}
else
{
frag << " vec4 base = vertexColor;\n";
}
if (stateMask & NORMAL_MAP)
{
frag << " vec3 normalDir = texture2D(normalMap, gl_TexCoord[0].st).xyz*2.0-1.0;\n";
}
#if !OSG_GLES2_FEATURES && !OSG_GLES3_FEATURES && !OSG_GL2_FEATURES
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
#endif
{
frag << " vec4 color = base;\n";
}
#if !OSG_GLES2_FEATURES && !OSG_GLES3_FEATURES && !OSG_GL2_FEATURES ShaderGenVisitor::ShaderGenVisitor():
if (stateMask & FOG) osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
{
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";
}
#endif
frag << " gl_FragColor = color;\n";
frag << "}\n";
std::string vertstr = vert.str();
std::string fragstr = frag.str();
OSG_DEBUG << "ShaderGenCache Vertex shader:\n" << vertstr << std::endl;
OSG_DEBUG << "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) : void ShaderGenVisitor::assignUberProgram(osg::StateSet *stateSet)
NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_stateCache(stateCache),
_state(new StateEx)
{ {
} if (stateSet)
{
stateSet->setAttribute(s_UberProgram.get());
stateSet->addUniform(new osg::Uniform("diffuseMap", 0));
void ShaderGenVisitor::setRootStateSet(osg::StateSet *stateSet) remapStateSet(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) void ShaderGenVisitor::apply(osg::Node &node)
{ {
osg::StateSet *stateSet = node.getStateSet(); osg::StateSet* stateSet = node.getStateSet();
if (stateSet) remapStateSet(stateSet);
if (stateSet)
_state->pushStateSet(stateSet);
traverse(node); traverse(node);
if (stateSet)
_state->popStateSet();
} }
void ShaderGenVisitor::apply(osg::Drawable &drawable)
void ShaderGenVisitor::remapStateSet(osg::StateSet* stateSet)
{ {
osg::StateSet *stateSet = drawable.getStateSet(); if (!stateSet) return;
if (stateSet)
_state->pushStateSet(stateSet);
update(&drawable); // remove any modes that won't be appropriate when using shaders, and remap them to the apppropriate Uniform/Define combination
if (stateSet)
_state->popStateSet(); osg::StateSet::ModeList& modes = stateSet->getModeList();
if (modes.count(GL_LIGHTING)>0)
{
osg::StateAttribute::GLModeValue lightingMode =modes[GL_LIGHTING];
stateSet->removeMode(GL_LIGHTING);
stateSet->removeMode(GL_LIGHT0);
stateSet->setDefine("GL_LIGHTING", lightingMode);
}
if (modes.count(GL_FOG)>0)
{
osg::StateAttribute::GLModeValue fogMode = modes[GL_FOG];
stateSet->removeMode(GL_FOG);
stateSet->setDefine("GL_FOG", fogMode);
}
if (!stateSet->getTextureModeList().empty())
{
osg::StateSet::ModeList& textureModes = stateSet->getTextureModeList()[0];
if (textureModes.count(GL_TEXTURE_2D)>0)
{
osg::StateAttribute::GLModeValue textureMode = textureModes[GL_TEXTURE_2D];
stateSet->removeTextureMode(0, GL_TEXTURE_2D);
stateSet->setDefine("GL_TEXTURE_2D", textureMode);
}
}
} }
void ShaderGenVisitor::update(osg::Drawable *drawable) } // namespace osgUtil
{
// update only geometry due to compatibility issues with user defined drawables
osg::Geometry *geometry = drawable->asGeometry();
#if 0
if (!geometry)
return;
#endif
StateEx *state = static_cast<StateEx *>(_state.get());
// skip nodes without state sets
if (state->getStateSetStackSize() == (_rootStateSet.valid() ? 1u : 0u))
return;
// skip state sets with already attached programs
if (state->getAttribute(osg::StateAttribute::PROGRAM))
return;
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!=0 &&
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());
// remove any modes that won't be appropriate when using shaders
if ((stateMask&ShaderGenCache::LIGHTING)!=0)
{
ss->removeMode(GL_LIGHTING);
ss->removeMode(GL_LIGHT0);
}
if ((stateMask&ShaderGenCache::FOG)!=0)
{
ss->removeMode(GL_FOG);
}
if ((stateMask&ShaderGenCache::DIFFUSE_MAP)!=0) ss->removeTextureMode(0, GL_TEXTURE_2D);
if ((stateMask&ShaderGenCache::NORMAL_MAP)!=0) ss->removeTextureMode(1, GL_TEXTURE_2D);
}

View File

@ -0,0 +1,35 @@
char shadergen_frag[] = "// ShaderGen shader\n"
"// new version\n"
"#ifdef GL_ES\n"
" precision highp float;\n"
"#endif\n"
"\n"
"#pragma import_defines(GL_LIGHTING, GL_TEXTURE_2D, GL_FOG)\n"
"\n"
"#if defined(GL_LIGHTING)\n"
"varying vec3 normalDir;\n"
"varying vec3 lightDir;\n"
"#endif\n"
"\n"
"#if defined(GL_LIGHTING) || defined(GL_FOG)\n"
"varying vec3 viewDir;\n"
"#endif\n"
"\n"
"varying vec4 vertexColor;\n"
"\n"
"#if defined(GL_TEXTURE_2D)\n"
"uniform sampler2D diffuseMap;\n"
"#endif\n"
"\n"
"void main()\n"
"{\n"
" vec4 color = vertexColor;\n"
"\n"
"#if defined(GL_TEXTURE_2D)\n"
" color = color * texture2D(diffuseMap, gl_TexCoord[0].st);\n"
"#endif\n"
"\n"
"\n"
" gl_FragColor = color;\n"
"}\n"
"\n";

View File

@ -0,0 +1,44 @@
char shadergen_vert[] = "// ShaderGen shader\n"
"// new version\n"
"#ifdef GL_ES\n"
" precision highp float;\n"
"#endif\n"
"\n"
"#pragma import_defines(GL_LIGHTING, GL_TEXTURE_2D, GL_FOG)\n"
"\n"
"#if defined(GL_LIGHTING)\n"
"varying vec3 normalDir;\n"
"varying vec3 lightDir;\n"
"#endif\n"
"\n"
"#if defined(GL_LIGHTING) || defined(GL_FOG)\n"
"varying vec3 viewDir;\n"
"#endif\n"
"\n"
"varying vec4 vertexColor;\n"
"\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = ftransform();\n"
"\n"
"#if defined(GL_TEXTURE_2D)\n"
" gl_TexCoord[0] = gl_MultiTexCoord0;\n"
"#endif\n"
"\n"
"#if defined(GL_LIGHTING) || defined(GL_FOG)\n"
" viewDir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"
"#endif\n"
"\n"
"#if defined(GL_LIGHTING)\n"
" normalDir = gl_NormalMatrix * gl_Normal;\n"
" vec4 lpos = gl_LightSource[0].position;\n"
" if (lpos.w == 0.0)\n"
" lightDir = lpos.xyz;\n"
" else\n"
" lightDir = lpos.xyz + viewDir;\n"
"#endif\n"
"\n"
" vertexColor = gl_Color;\n"
"}\n"
"\n";