From 5571c361dcb3fcebe104086de0f91e527339ac54 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 13 Dec 2013 12:38:01 +0000 Subject: [PATCH] Implemented ability to switch between different shaders with new MultipassTechnique --- examples/osgvolume/osgvolume.cpp | 2 - include/osgVolume/MultipassTechnique | 16 ++ src/osg/Program.cpp | 15 +- src/osgVolume/MultipassTechnique.cpp | 368 ++++++++++++++++++++------- src/osgVolume/Property.cpp | 8 +- 5 files changed, 318 insertions(+), 91 deletions(-) diff --git a/examples/osgvolume/osgvolume.cpp b/examples/osgvolume/osgvolume.cpp index be9ef0c1f..6d46fb94a 100644 --- a/examples/osgvolume/osgvolume.cpp +++ b/examples/osgvolume/osgvolume.cpp @@ -1105,8 +1105,6 @@ int main( int argc, char **argv ) cp->addProperty(tfp); } - cp->addProperty(isop); - sp->addProperty(cp); } diff --git a/include/osgVolume/MultipassTechnique b/include/osgVolume/MultipassTechnique index beb2ac6db..282ed7b69 100644 --- a/include/osgVolume/MultipassTechnique +++ b/include/osgVolume/MultipassTechnique @@ -53,6 +53,22 @@ class OSGVOLUME_EXPORT MultipassTechnique : public VolumeTechnique ModelViewMatrixMap _modelViewMatrixMap; osg::ref_ptr _whenMovingStateSet; + + osg::StateSet* createStateSet(osg::StateSet* statesetPrototype, osg::Program* programPrototype, osg::Shader* shaderToAdd1=0, osg::Shader* shaderToAdd2=0); + + enum ShaderMask + { + FRONT_SHADERS = 1, + BACK_SHADERS = 2, + STANDARD_SHADERS = 4, + LIT_SHADERS = 8, + ISO_SHADERS = 16, + MIP_SHADERS = 32, + TF_SHADERS = 64 + }; + + typedef std::map > StateSetMap; + StateSetMap _stateSetMap; }; } diff --git a/src/osg/Program.cpp b/src/osg/Program.cpp index bfa8bd83d..79e410ee1 100644 --- a/src/osg/Program.cpp +++ b/src/osg/Program.cpp @@ -145,9 +145,20 @@ Program::Program() : Program::Program(const Program& rhs, const osg::CopyOp& copyop): osg::StateAttribute(rhs, copyop) { - for( unsigned int shaderIndex=0; shaderIndex < rhs.getNumShaders(); ++shaderIndex ) + + if ((copyop.getCopyFlags()&osg::CopyOp::DEEP_COPY_STATEATTRIBUTES)!=0) { - addShader( new osg::Shader( *rhs.getShader( shaderIndex ), copyop ) ); + for( unsigned int shaderIndex=0; shaderIndex < rhs.getNumShaders(); ++shaderIndex ) + { + addShader( new osg::Shader( *rhs.getShader( shaderIndex ), copyop ) ); + } + } + else + { + for( unsigned int shaderIndex=0; shaderIndex < rhs.getNumShaders(); ++shaderIndex ) + { + addShader( const_cast(rhs.getShader( shaderIndex )) ); + } } const osg::Program::AttribBindingList &abl = rhs.getAttribBindingList(); diff --git a/src/osgVolume/MultipassTechnique.cpp b/src/osgVolume/MultipassTechnique.cpp index 4304c2163..0a94ba419 100644 --- a/src/osgVolume/MultipassTechnique.cpp +++ b/src/osgVolume/MultipassTechnique.cpp @@ -48,6 +48,17 @@ MultipassTechnique::~MultipassTechnique() { } +osg::StateSet* MultipassTechnique::createStateSet(osg::StateSet* statesetPrototype, osg::Program* programPrototype, osg::Shader* shaderToAdd1, osg::Shader* shaderToAdd2) +{ + osg::ref_ptr stateset = osg::clone(statesetPrototype, osg::CopyOp::SHALLOW_COPY); + osg::ref_ptr program = osg::clone(programPrototype, osg::CopyOp::SHALLOW_COPY); + stateset->setAttribute(program.get()); + if (shaderToAdd1) program->addShader(shaderToAdd1); + if (shaderToAdd2) program->addShader(shaderToAdd2); + + return stateset.release(); +} + void MultipassTechnique::init() { OSG_INFO<<"MultipassTechnique::init()"<getLayer()->getProperty()) { _volumeTile->getLayer()->getProperty()->accept(cpv); @@ -142,6 +153,7 @@ void MultipassTechnique::init() } _transform = new osg::MatrixTransform; + _transform->addChild(geode.get()); // handle locators Locator* masterLocator = _volumeTile->getLocator(); @@ -215,7 +227,6 @@ void MultipassTechnique::init() tf = dynamic_cast(cpv._tfProperty->getTransferFunction()); } -#if 1 osg::ref_ptr texgen = new osg::TexGen; texgen->setMode(osg::TexGen::OBJECT_LINEAR); texgen->setPlanesFromMatrix( geometryMatrix * osg::Matrix::inverse(imageMatrix)); @@ -231,7 +242,6 @@ void MultipassTechnique::init() } stateset->setTextureAttributeAndModes(texgenTextureUnit, texgen.get(), osg::StateAttribute::ON); -#endif } @@ -317,6 +327,11 @@ void MultipassTechnique::init() } + // creates CullFace attributes to apply to front/back StateSet configurations. + osg::ref_ptr front_CullFace = new osg::CullFace(osg::CullFace::BACK); + osg::ref_ptr back_CullFace = new osg::CullFace(osg::CullFace::FRONT); + + osg::ref_ptr computeRayColorShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_compute_ray_color.frag"); #if 0 @@ -327,95 +342,222 @@ void MultipassTechnique::init() } #endif - // set up the renderin of the front faces + osg::ref_ptr main_vertexShader = osgDB::readRefShaderFile(osg::Shader::VERTEX, "shaders/volume_multipass.vert"); +#if 0 + if (!main_vertexShader) { - osg::ref_ptr front_face_group = new osg::Group; - front_face_group->addChild(geode.get()); - _transform->addChild(front_face_group.get()); - - osg::ref_ptr front_face_stateset = front_face_group->getOrCreateStateSet(); - front_face_stateset->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON); - - osg::ref_ptr program = new osg::Program; - front_face_stateset->setAttribute(program); - - // get vertex shaders from source - osg::ref_ptr vertexShader = osgDB::readRefShaderFile(osg::Shader::VERTEX, "shaders/volume_multipass_front.vert"); - if (vertexShader.valid()) - { - program->addShader(vertexShader.get()); - } -#if 0 - else - { - #include "Shaders/volume_color_depth_vert.cpp" - program->addShader(new osg::Shader(osg::Shader::VERTEX, volume_color_depth_vert)); - } -#endif - // get fragment shaders from source - osg::ref_ptr fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_front.frag"); - if (fragmentShader.valid()) - { - program->addShader(fragmentShader.get()); - } -#if 0 - else - { - #include "Shaders/volume_color_depth_frag.cpp" - program->addShader(new osg::Shader(osg::Shader::FRAGMENT, volume_color_depth_frag)); - } + #include "Shaders/volume_multipass_vert.cpp" + main_vertexShader = new osg::Shader(osg::Shader::VERTEX, volume_multipass_vert)); + } #endif - if (computeRayColorShader.valid()) + osg::ref_ptr front_main_fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_front.frag"); +#if 0 + if (!front_main_fragmentShader) + { + #include "Shaders/volume_multipass_front_frag.cpp" + front_main_fragmentShader = new osg::Shader(osg::Shader::VERTEX, volume_multipass_front_frag)); + } +#endif + + + osg::ref_ptr back_main_fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_back.frag"); +#if 0 + if (!back_main_fragmentShader) + { + #include "Shaders/volume_multipass_back_frag.cpp" + back_main_fragmentShader = new osg::Shader(osg::Shader::VERTEX, volume_multipass_back_frag)); + } +#endif + + // clear any previous settings + _stateSetMap.clear(); + + osg::ref_ptr front_stateset_prototype = new osg::StateSet; + osg::ref_ptr front_program_prototype = new osg::Program; + { + front_stateset_prototype->setAttributeAndModes(front_CullFace.get(), osg::StateAttribute::ON); + + front_program_prototype->addShader(main_vertexShader.get()); + front_program_prototype->addShader(front_main_fragmentShader.get()); + front_program_prototype->addShader(computeRayColorShader.get()); + } + + osg::ref_ptr back_stateset_prototype = new osg::StateSet; + osg::ref_ptr back_program_prototype = new osg::Program; + { + back_stateset_prototype->setAttributeAndModes(back_CullFace.get(), osg::StateAttribute::ON); + + back_program_prototype->addShader(main_vertexShader.get()); + back_program_prototype->addShader(back_main_fragmentShader.get()); + back_program_prototype->addShader(computeRayColorShader.get()); + } + + // STANDARD_SHADERS + { + // STANDARD_SHADERS without TransferFunction { - program->addShader(computeRayColorShader.get()); + osg::ref_ptr accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_standard.frag"); + #if 0 + if (!accumulateSamplesShader) + { + #include "Shaders/volume_accumulateSamples_standard_frag.cpp"; + accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_standard_frag); + } + #endif + + // front + _stateSetMap[STANDARD_SHADERS|FRONT_SHADERS] = createStateSet(front_stateset_prototype.get(), front_program_prototype.get(), accumulateSamplesShader.get()); + + // back + _stateSetMap[STANDARD_SHADERS|BACK_SHADERS] = createStateSet(back_stateset_prototype.get(), back_program_prototype.get(), accumulateSamplesShader.get()); + } + + // STANDARD_SHADERS with TransferFunction + if (tf) + { + osg::ref_ptr accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_standard_tf.frag"); + #if 0 + if (!accumulateSamplesShader) + { + #include "Shaders/volume_accumulateSamples_standard_tf_frag.cpp"; + accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_standard_tf_frag); + } + #endif + + + // front + _stateSetMap[STANDARD_SHADERS|FRONT_SHADERS|TF_SHADERS] = createStateSet(front_stateset_prototype.get(), front_program_prototype.get(), accumulateSamplesShader.get()); + + // back + _stateSetMap[STANDARD_SHADERS|BACK_SHADERS|TF_SHADERS] = createStateSet(back_stateset_prototype.get(), back_program_prototype.get(), accumulateSamplesShader.get()); } } - - // set up the rendering of the back faces + // ISO_SHADERS + if (cpv._isoProperty.valid()) { - osg::ref_ptr back_face_group = new osg::Group; - back_face_group->addChild(geode.get()); - _transform->addChild(back_face_group.get()); + // ISO_SHADERS without TransferFunction + { + osg::ref_ptr accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_iso.frag"); + #if 0 + if (!accumulateSamplesShader) + { + #include "Shaders/volume_accumulateSamples_iso_frag.cpp"; + accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_iso_frag); + } + #endif - osg::ref_ptr back_face_stateset = back_face_group->getOrCreateStateSet(); - back_face_stateset->setAttributeAndModes(new osg::CullFace(osg::CullFace::FRONT), osg::StateAttribute::ON); + // front + _stateSetMap[ISO_SHADERS|FRONT_SHADERS] = createStateSet(front_stateset_prototype.get(), front_program_prototype.get(), accumulateSamplesShader.get()); - osg::ref_ptr program = new osg::Program; - back_face_stateset->setAttribute(program); - - // get vertex shaders from source - osg::ref_ptr vertexShader = osgDB::readRefShaderFile(osg::Shader::VERTEX, "shaders/volume_multipass_back.vert"); - if (vertexShader.valid()) - { - program->addShader(vertexShader.get()); - } -#if 0 - else - { - #include "Shaders/volume_color_depth_vert.cpp" - program->addShader(new osg::Shader(osg::Shader::VERTEX, volume_color_depth_vert)); - } -#endif - // get fragment shaders from source - osg::ref_ptr fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_back.frag"); - if (fragmentShader.valid()) - { - program->addShader(fragmentShader.get()); - } -#if 0 - else - { - #include "Shaders/volume_color_depth_frag.cpp" - program->addShader(new osg::Shader(osg::Shader::FRAGMENT, volume_color_depth_frag)); - } -#endif - if (computeRayColorShader.valid()) - { - program->addShader(computeRayColorShader.get()); + // back + _stateSetMap[ISO_SHADERS|BACK_SHADERS] = createStateSet(back_stateset_prototype.get(), back_program_prototype.get(), accumulateSamplesShader.get()); } + // ISO_SHADERS with TransferFunction + if (tf) + { + osg::ref_ptr accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_iso_tf.frag"); + #if 0 + if (!accumulateSamplesShader) + { + #include "Shaders/volume_accumulateSamples_standard_iso_tf_frag.cpp"; + accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_standard_iso_tf_frag); + } + #endif + + + // front + _stateSetMap[ISO_SHADERS|FRONT_SHADERS|TF_SHADERS] = createStateSet(front_stateset_prototype.get(), front_program_prototype.get(), accumulateSamplesShader.get()); + + // back + _stateSetMap[ISO_SHADERS|BACK_SHADERS|TF_SHADERS] = createStateSet(back_stateset_prototype.get(), back_program_prototype.get(), accumulateSamplesShader.get()); + } + } + + // MIP_SHADERS + if (cpv._mipProperty.valid()) + { + // MIP_SHADERS without TransferFunction + { + osg::ref_ptr accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_mip.frag"); + #if 0 + if (!accumulateSamplesShader) + { + #include "Shaders/volume_accumulateSamples_mip_frag.cpp"; + accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_mip_frag); + } + #endif + + // front + _stateSetMap[MIP_SHADERS|FRONT_SHADERS] = createStateSet(front_stateset_prototype.get(), front_program_prototype.get(), accumulateSamplesShader.get()); + + // back + _stateSetMap[MIP_SHADERS|BACK_SHADERS] = createStateSet(back_stateset_prototype.get(), back_program_prototype.get(), accumulateSamplesShader.get()); + } + + // MIP_SHADERS with TransferFunction + if (tf) + { + osg::ref_ptr accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_mip_tf.frag"); + #if 0 + if (!accumulateSamplesShader) + { + #include "Shaders/volume_accumulateSamples_standard_mip_tf_frag.cpp"; + accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_standard_mip_tf_frag); + } + #endif + + + // front + _stateSetMap[MIP_SHADERS|FRONT_SHADERS|TF_SHADERS] = createStateSet(front_stateset_prototype.get(), front_program_prototype.get(), accumulateSamplesShader.get()); + + // back + _stateSetMap[MIP_SHADERS|BACK_SHADERS|TF_SHADERS] = createStateSet(back_stateset_prototype.get(), back_program_prototype.get(), accumulateSamplesShader.get()); + } + } + + // LIT_SHADERS + if (cpv._lightingProperty.valid()) + { + // LIT_SHADERS without TransferFunction + { + osg::ref_ptr accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_lit.frag"); + #if 0 + if (!accumulateSamplesShader) + { + #include "Shaders/volume_accumulateSamples_lit_frag.cpp"; + accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_lit_frag); + } + #endif + + // front + _stateSetMap[LIT_SHADERS|FRONT_SHADERS] = createStateSet(front_stateset_prototype.get(), front_program_prototype.get(), accumulateSamplesShader.get()); + + // back + _stateSetMap[LIT_SHADERS|BACK_SHADERS] = createStateSet(back_stateset_prototype.get(), back_program_prototype.get(), accumulateSamplesShader.get()); + } + + // MIP_SHADERS with TransferFunction + if (tf) + { + osg::ref_ptr accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_lit_tf.frag"); + #if 0 + if (!accumulateSamplesShader) + { + #include "Shaders/volume_accumulateSamples_standard_lit_tf_frag.cpp"; + accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_standard_lit_tf_frag); + } + #endif + + + // front + _stateSetMap[LIT_SHADERS|FRONT_SHADERS|TF_SHADERS] = createStateSet(front_stateset_prototype.get(), front_program_prototype.get(), accumulateSamplesShader.get()); + + // back + _stateSetMap[LIT_SHADERS|BACK_SHADERS|TF_SHADERS] = createStateSet(back_stateset_prototype.get(), back_program_prototype.get(), accumulateSamplesShader.get()); + } } @@ -441,17 +583,73 @@ void MultipassTechnique::cull(osgUtil::CullVisitor* cv) if (postTraversal) { - if (_whenMovingStateSet.valid() && isMoving(cv)) + + int shaderMask = 0; + if (_volumeTile->getLayer()->getProperty()) { - OSG_NOTICE<<"Using MovingStateSet"<pushStateSet(_whenMovingStateSet.get()); + CollectPropertiesVisitor cpv; + _volumeTile->getLayer()->getProperty()->accept(cpv); + + if (cpv._tfProperty.valid()) + { + shaderMask |= TF_SHADERS; + } + + if (cpv._isoProperty.valid()) + { + shaderMask |= ISO_SHADERS; + } + else if (cpv._mipProperty.valid()) + { + shaderMask |= MIP_SHADERS; + } + else if (cpv._lightingProperty.valid()) + { + shaderMask |= LIT_SHADERS; + } + else + { + shaderMask |= STANDARD_SHADERS; + } + } + + int shaderMaskFront = shaderMask | FRONT_SHADERS; + int shaderMaskBack = shaderMask | BACK_SHADERS; + + OSG_NOTICE<<"shaderMaskFront "< front_stateset = _stateSetMap[shaderMaskFront]; + osg::ref_ptr back_stateset = _stateSetMap[shaderMaskBack]; + osg::ref_ptr moving_stateset = (_whenMovingStateSet.valid() && isMoving(cv)) ? _whenMovingStateSet : 0; + + if (moving_stateset.valid()) + { + // OSG_NOTICE<<"Using MovingStateSet"<pushStateSet(moving_stateset.get()); + } + + if (front_stateset.valid()) + { + OSG_NOTICE<<"Have front stateset"<pushStateSet(front_stateset.get()); _transform->accept(*cv); cv->popStateSet(); } - else + + if (back_stateset.valid()) { - OSG_NOTICE<<"NOT using MovingStateSet"<pushStateSet(back_stateset.get()); _transform->accept(*cv); + cv->popStateSet(); + } + + if (moving_stateset.valid()) + { + // OSG_NOTICE<<"Using MovingStateSet"<popStateSet(); } } else diff --git a/src/osgVolume/Property.cpp b/src/osgVolume/Property.cpp index 6639ec9c6..20f993418 100644 --- a/src/osgVolume/Property.cpp +++ b/src/osgVolume/Property.cpp @@ -13,6 +13,7 @@ #include #include +#include using namespace osgVolume; @@ -401,8 +402,11 @@ bool PropertyAdjustmentCallback::handle(const osgGA::GUIEventAdapter& ea,osgGA:: property->accept(csv); if (csv._switchModified) { - tile->setDirty(true); - tile->init(); + if (dynamic_cast(tile->getVolumeTechnique())) + { + tile->setDirty(true); + tile->init(); + } } } else if (ea.getKey()==_transparencyKey) _updateTransparency = passOnUpdates = true;