diff --git a/examples/osgshadercomposition/osgshadercomposition.cpp b/examples/osgshadercomposition/osgshadercomposition.cpp index ec759a12d..832329f4d 100644 --- a/examples/osgshadercomposition/osgshadercomposition.cpp +++ b/examples/osgshadercomposition/osgshadercomposition.cpp @@ -29,6 +29,30 @@ osg::Node* createSceneGraph(osg::ArgumentParser& arguments) osg::ShaderAttribute* sa = new osg::ShaderAttribute; stateset->setAttribute(sa); + { + const char shader_str[] = + "vec4 colour()\n" + "{\n" + " return vec4(1.0,0.5,1.0,1.0);\n" + "}\n"; + + osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, shader_str); + vertex_shader->addCodeInjection(-1,"varying vec4 c;\n"); + vertex_shader->addCodeInjection(-1,"vec4 colour();\n"); + vertex_shader->addCodeInjection(0,"gl_Position = ftransform();\n"); + vertex_shader->addCodeInjection(0,"c = colour();\n"); + + sa->addShader(vertex_shader); + } + + { + osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT); + fragment_shader->addCodeInjection(-1,"varying vec4 c;\n"); + fragment_shader->addCodeInjection(0,"gl_FragColor = c;\n"); + + sa->addShader(fragment_shader); + } + return node; } @@ -43,5 +67,17 @@ int main( int argc, char **argv ) viewer.setSceneData(scenegraph.get()); + viewer.realize(); + + // enable shader composition + osgViewer::Viewer::Windows windows; + viewer.getWindows(windows); + for(osgViewer::Viewer::Windows::iterator itr = windows.begin(); + itr != windows.end(); + ++itr) + { + (*itr)->getState()->setShaderCompositionEnabled(true); + } + return viewer.run(); } diff --git a/include/osg/Shader b/include/osg/Shader index ed6a43835..1d5eb8a0a 100644 --- a/include/osg/Shader +++ b/include/osg/Shader @@ -25,6 +25,7 @@ #include #include +#include namespace osg { @@ -143,6 +144,24 @@ class OSG_EXPORT Shader : public osg::Object bool loadShaderSourceFromFile( const std::string& fileName ); + /** The code injection map used when generating the main shader during main shader composition.*/ + typedef std::multimap CodeInjectionMap; + + /** Add code injection that will be placed in the main shader to enable support for this shader. + * The position is set up so that code to be inserted before the main() will have a negative value, + * a position between 0 and 1.0 will be inserted in main() and a position greater than 1.0 will + * be placed after the main(). + * During shader composition all the code injections are sorted in ascending order and then + * placed in the appropriate section of the main shader. */ + void addCodeInjection(float position, const std::string& code) { _codeInjectionMap.insert(CodeInjectionMap::value_type(position, code)); } + + /** Get the code injection map.*/ + CodeInjectionMap& getCodeInjectionMap() { return _codeInjectionMap; } + + /** Get the const code injection map.*/ + const CodeInjectionMap& getCodeInjectionMap() const { return _codeInjectionMap; } + + /** Resize any per context GLObject buffers to specified size. */ virtual void resizeGLObjectBuffers(unsigned int maxSize); @@ -242,6 +261,8 @@ class OSG_EXPORT Shader : public osg::Object std::string _shaderFileName; std::string _shaderSource; osg::ref_ptr _shaderBinary; + + CodeInjectionMap _codeInjectionMap; /** osg::Programs that this osg::Shader is attached to */ typedef std::set< osg::Program* > ProgramSet; diff --git a/include/osg/ShaderComposer b/include/osg/ShaderComposer index ff47209c6..97ea26cd7 100644 --- a/include/osg/ShaderComposer +++ b/include/osg/ShaderComposer @@ -38,11 +38,13 @@ class OSG_EXPORT ShaderComposer : public osg::Object typedef std::vector< const osg::Shader* > Shaders; virtual osg::Shader* composeMain(const Shaders& shaders); + virtual void addShaderToProgram(Program* program, const Shaders& shaders); protected: virtual ~ShaderComposer(); + typedef std::map< ShaderComponents, ref_ptr > ProgramMap; ProgramMap _programMap; diff --git a/include/osg/State b/include/osg/State index 634f64704..d081deb43 100644 --- a/include/osg/State +++ b/include/osg/State @@ -142,6 +142,12 @@ class OSG_EXPORT State : public Referenced, public Observer inline unsigned int getContextID() const { return _contextID; } + /* Set whether shader composition is enabled.*/ + void setShaderCompositionEnabled(bool flag) { _shaderCompositionEnabled = flag; } + + /* Get whether shader composition is enabled.*/ + bool getShaderCompositionEnabled() const { return _shaderCompositionEnabled; } + /** Set the ShaderComposor object that implements shader composition.*/ void setShaderComposer(ShaderComposer* sc) { _shaderComposer = sc; } @@ -250,6 +256,8 @@ class OSG_EXPORT State : public Referenced, public Observer */ void apply(); + /** Apply any shader composed state.*/ + void applyShaderComposition(); /** Set whether a particular OpenGL mode is valid in the current graphics context. * Use to disable OpenGL modes that are not supported by current graphics drivers/context.*/ diff --git a/src/osg/Shader.cpp b/src/osg/Shader.cpp index 860fcfa17..136401aee 100644 --- a/src/osg/Shader.cpp +++ b/src/osg/Shader.cpp @@ -201,7 +201,8 @@ Shader::Shader(const Shader& rhs, const osg::CopyOp& copyop): _type(rhs._type), _shaderFileName(rhs._shaderFileName), _shaderSource(rhs._shaderSource), - _shaderBinary(rhs._shaderBinary) + _shaderBinary(rhs._shaderBinary), + _codeInjectionMap(rhs._codeInjectionMap) { } diff --git a/src/osg/ShaderComposer.cpp b/src/osg/ShaderComposer.cpp index 662d29bf2..aef3238e2 100644 --- a/src/osg/ShaderComposer.cpp +++ b/src/osg/ShaderComposer.cpp @@ -77,54 +77,19 @@ osg::Program* ShaderComposer::getOrCreateProgram(const ShaderComponents& shaderC if (!vertexShaders.empty()) { - ShaderMainMap::iterator itr = _shaderMainMap.find(vertexShaders); - if (itr == _shaderMainMap.end()) - { - // no vertex shader in map yet, need to compose a new main shader - osg::Shader* mainShader = composeMain(vertexShaders); - _shaderMainMap[vertexShaders] = mainShader; - program->addShader(mainShader); - } - else - { - program->addShader(itr->second.get()); - } + addShaderToProgram(program.get(), vertexShaders); } if (!geometryShaders.empty()) { - ShaderMainMap::iterator itr = _shaderMainMap.find(geometryShaders); - if (itr == _shaderMainMap.end()) - { - // no vertex shader in map yet, need to compose a new main shader - osg::Shader* mainShader = composeMain(geometryShaders); - _shaderMainMap[geometryShaders] = mainShader; - program->addShader(mainShader); - } - else - { - program->addShader(itr->second.get()); - } + addShaderToProgram(program.get(), geometryShaders); } - if (!fragmentShaders.empty()) { - ShaderMainMap::iterator itr = _shaderMainMap.find(fragmentShaders); - if (itr == _shaderMainMap.end()) - { - // no vertex shader in map yet, need to compose a new main shader - osg::Shader* mainShader = composeMain(fragmentShaders); - _shaderMainMap[fragmentShaders] = mainShader; - program->addShader(mainShader); - } - else - { - program->addShader(itr->second.get()); - } + addShaderToProgram(program.get(), fragmentShaders); } - // assign newly created program to map. _programMap[shaderComponents] = program; @@ -133,8 +98,93 @@ osg::Program* ShaderComposer::getOrCreateProgram(const ShaderComponents& shaderC return program.get(); } +void ShaderComposer::addShaderToProgram(Program* program, const Shaders& shaders) +{ + ShaderMainMap::iterator itr = _shaderMainMap.find(shaders); + if (itr == _shaderMainMap.end()) + { + // no vertex shader in map yet, need to compose a new main shader + osg::Shader* mainShader = composeMain(shaders); + _shaderMainMap[shaders] = mainShader; + program->addShader(mainShader); + } + else + { + program->addShader(itr->second.get()); + } + + for(Shaders::const_iterator itr = shaders.begin(); + itr != shaders.end(); + ++itr) + { + Shader* shader = const_cast(*itr); + if (!(shader->getShaderSource().empty()) || shader->getShaderBinary()) + { + program->addShader(shader); + } + } +} + osg::Shader* ShaderComposer::composeMain(const Shaders& shaders) { OSG_NOTICE<<"ShaderComposer::composeMain(Shaders) shaders.size()=="<getType(); + } + else if (type != shader->getType()) + { + OSG_NOTICE<<"Warning:ShaderComposer::composeMain() mixing different types of Shaders prohibited."<getCodeInjectionMap(); + for(Shader::CodeInjectionMap::const_iterator citr = cim.begin(); + citr != cim.end(); + ++citr) + { + codeInjectionMap.insert(*citr); + } + } + + // collect together the different parts of the main shader + std::string before_main; + std::string in_main; + std::string after_main; + + for(Shader::CodeInjectionMap::iterator citr = codeInjectionMap.begin(); + citr != codeInjectionMap.end(); + ++citr) + { + float position = citr->first; + if (position<0.0) before_main += citr->second; + else if (position<=1.0) in_main += citr->second; + else after_main += citr->second; + } + + // assembly the final main shader source + std::string full_source; + full_source += before_main; + full_source += std::string("void main(void)\n"); + full_source += std::string("{\n"); + full_source += in_main; + full_source += std::string("}\n"); + full_source += after_main; + + + ref_ptr mainShader = new Shader(type, full_source); + + _shaderMainMap[shaders] = mainShader; + + return mainShader.get(); } diff --git a/src/osg/State.cpp b/src/osg/State.cpp index 7122a4178..887b90e12 100644 --- a/src/osg/State.cpp +++ b/src/osg/State.cpp @@ -42,7 +42,7 @@ State::State(): _graphicsContext = 0; _contextID = 0; - _shaderCompositionEnabled = false; // true; + _shaderCompositionEnabled = false; _shaderCompositionDirty = true; _shaderComposer = new ShaderComposer; _currentShaderCompositionProgram = 0L; @@ -515,21 +515,7 @@ void State::apply(const StateSet* dstate) else if (unit<_textureAttributeMapList.size()) applyAttributeMapOnTexUnit(unit,_textureAttributeMapList[unit]); } - if (_shaderCompositionEnabled) - { - if (_shaderCompositionDirty) - { - // build lits of current ShaderComponents - ShaderComponents shaderComponents; - _currentShaderCompositionProgram = _shaderComposer->getOrCreateProgram(shaderComponents); - } - - if (_currentShaderCompositionProgram) - { - Program::PerContextProgram* pcp = _currentShaderCompositionProgram->getPCP(_contextID); - if (_lastAppliedProgramObject != pcp) applyAttribute(_currentShaderCompositionProgram); - } - } + applyShaderComposition(); applyUniformList(_uniformMap,dstate->getUniformList()); } @@ -562,11 +548,45 @@ void State::apply() if (unit<_textureAttributeMapList.size()) applyAttributeMapOnTexUnit(unit,_textureAttributeMapList[unit]); } + applyShaderComposition(); + applyUniformMap(_uniformMap); if (_checkGLErrors==ONCE_PER_ATTRIBUTE) checkGLErrors("end of State::apply()"); } +void State::applyShaderComposition() +{ + if (_shaderCompositionEnabled) + { + if (_shaderCompositionDirty) + { + // build lits of current ShaderComponents + ShaderComponents shaderComponents; + + for(AttributeMap::iterator itr = _attributeMap.begin(); + itr != _attributeMap.end(); + ++itr) + { + AttributeStack& as = itr->second; + if (as.last_applied_shadercomponent) + { + shaderComponents.push_back(const_cast(as.last_applied_shadercomponent)); + } + } + + _currentShaderCompositionProgram = _shaderComposer->getOrCreateProgram(shaderComponents); + } + + if (_currentShaderCompositionProgram) + { + Program::PerContextProgram* pcp = _currentShaderCompositionProgram->getPCP(_contextID); + if (_lastAppliedProgramObject != pcp) applyAttribute(_currentShaderCompositionProgram); + } + } +} + + void State::haveAppliedMode(StateAttribute::GLMode mode,StateAttribute::GLModeValue value) { haveAppliedMode(_modeMap,mode,value);