From 4447190dd6cb926341547c001bcaecedffb3bf81 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 14 Mar 2018 08:22:45 +0000 Subject: [PATCH] Refactored osgUtil::ShaderGen to use #pragma(tic) shader composition. --- examples/osgshadergen/osgshadergen.cpp | 51 ++-- include/osgUtil/ShaderGen | 46 +-- src/osgUtil/ShaderGen.cpp | 406 ++++--------------------- src/osgUtil/shaders/shadergen_frag.cpp | 35 +++ src/osgUtil/shaders/shadergen_vert.cpp | 44 +++ 5 files changed, 164 insertions(+), 418 deletions(-) create mode 100644 src/osgUtil/shaders/shadergen_frag.cpp create mode 100644 src/osgUtil/shaders/shadergen_vert.cpp diff --git a/examples/osgshadergen/osgshadergen.cpp b/examples/osgshadergen/osgshadergen.cpp index 042f00a02..f57eed04e 100644 --- a/examples/osgshadergen/osgshadergen.cpp +++ b/examples/osgshadergen/osgshadergen.cpp @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -26,31 +27,6 @@ #include -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) { @@ -64,6 +40,8 @@ int main(int argc, char** argv) osgViewer::Viewer viewer(arguments); + + unsigned int helpType = 0; if ((helpType = arguments.readHelpType())) { @@ -134,11 +112,10 @@ int main(int argc, char** argv) // 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); + + + std::string outputFilename; + if (arguments.read("-o", outputFilename)) {} // load the data osg::ref_ptr loadedModel = osgDB::readRefNodeFiles(arguments); @@ -148,6 +125,20 @@ int main(int argc, char** argv) 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. arguments.reportRemainingOptionsAsUnrecognized(); diff --git a/include/osgUtil/ShaderGen b/include/osgUtil/ShaderGen index f30a9cd78..60c059c19 100644 --- a/include/osgUtil/ShaderGen +++ b/include/osgUtil/ShaderGen @@ -26,57 +26,19 @@ 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 > 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 { public: ShaderGenVisitor(); - ShaderGenVisitor(ShaderGenCache *stateCache); - void setStateCache(ShaderGenCache *stateCache) { _stateCache = stateCache; } - ShaderGenCache *getStateCache() const { return _stateCache.get(); } + /// assign default uber program to specified StateSet - typically the root node of the scene graph or the view's Camera + void assignUberProgram(osg::StateSet *stateSet); - /// 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::Node &node); - void apply(osg::Drawable &drawable); - - void reset(); + void remapStateSet(osg::StateSet* stateSet); protected: - void update(osg::Drawable *drawable); - - osg::ref_ptr _stateCache; - osg::ref_ptr _state; - osg::ref_ptr _rootStateSet; }; } diff --git a/src/osgUtil/ShaderGen.cpp b/src/osgUtil/ShaderGen.cpp index 4fb248e6d..e67eaba58 100644 --- a/src/osgUtil/ShaderGen.cpp +++ b/src/osgUtil/ShaderGen.cpp @@ -22,379 +22,93 @@ #include #include +#include "shaders/shadergen_vert.cpp" +#include "shaders/shadergen_frag.cpp" + using namespace osgUtil; namespace osgUtil { -/// State extended by mode/attribute accessors -class StateEx : public osg::State +osg::ref_ptr s_UberProgram; + +struct UberProgramConstructor { -public: - StateEx() : State() {} - - osg::StateAttribute::GLModeValue getMode(osg::StateAttribute::GLMode mode, - osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const + UberProgramConstructor() { - 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(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(it->second.attributeVec.back().first) : 0; + 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)); } }; -} - -void ShaderGenCache::setStateSet(int stateMask, osg::StateSet *stateSet) -{ - OpenThreads::ScopedLock lock(_mutex); - _stateSetMap[stateMask] = stateSet; -} - -osg::StateSet *ShaderGenCache::getStateSet(int stateMask) const -{ - OpenThreads::ScopedLock 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 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"; - } +UberProgramConstructor s_UberProgramConstructor; -#if !OSG_GLES2_FEATURES && !OSG_GLES3_FEATURES && !OSG_GL2_FEATURES - 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"; - } -#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(): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) { } -ShaderGenVisitor::ShaderGenVisitor(ShaderGenCache *stateCache) : - NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), - _stateCache(stateCache), - _state(new StateEx) +void ShaderGenVisitor::assignUberProgram(osg::StateSet *stateSet) { -} + if (stateSet) + { + stateSet->setAttribute(s_UberProgram.get()); + stateSet->addUniform(new osg::Uniform("diffuseMap", 0)); -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()); + remapStateSet(stateSet); + } } void ShaderGenVisitor::apply(osg::Node &node) { - osg::StateSet *stateSet = node.getStateSet(); - - if (stateSet) - _state->pushStateSet(stateSet); + osg::StateSet* stateSet = node.getStateSet(); + if (stateSet) remapStateSet(stateSet); 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) - _state->pushStateSet(stateSet); + if (!stateSet) return; - 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) -{ - // 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(_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(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); -} +} // namespace osgUtil \ No newline at end of file diff --git a/src/osgUtil/shaders/shadergen_frag.cpp b/src/osgUtil/shaders/shadergen_frag.cpp new file mode 100644 index 000000000..5018219f3 --- /dev/null +++ b/src/osgUtil/shaders/shadergen_frag.cpp @@ -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"; diff --git a/src/osgUtil/shaders/shadergen_vert.cpp b/src/osgUtil/shaders/shadergen_vert.cpp new file mode 100644 index 000000000..08e8dfc8b --- /dev/null +++ b/src/osgUtil/shaders/shadergen_vert.cpp @@ -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";