How to place an Image inside an Image?

classic Classic list List threaded Threaded
6 messages Options
joseph joseph
Reply | Threaded
Open this post in threaded view
|

How to place an Image inside an Image?

Hello Everyone!

Ok, so I need to place an image inside an image, such that, any transparent pixels of the inner image 'cut a hole' past the outer image (subsequently displaying the background layer behind both images).

I can do this in OSG using Stencils (applied to a Geode), but it seems osgEarth only uses the ImageOverlay class to display Images.  So my question is either:

a) How to apply an osg::Stenicl/osg::Geode to an osgEarth::ImageOverlay?

or, b) Does ImageOverlay have its own method of Stenciling, or is there a way to 'cut a hole' in an existing ImageOverlay?

or c) Some other method I'm not aware of?


I'm coding in C++

Thank you so much for sharing your wisdom & insight!

// joe
gwaldron gwaldron
Reply | Threaded
Open this post in threaded view
|

Re: How to place an Image inside an Image?

Hi Joseph,

The ImageOverlay is just a wrapper around the more generic DrapeableNode, which renders its subgraph to a texture and then projects that texture on to the earth's surface. Maybe that will work for you.
Glenn Waldron / Pelican Mapping
joseph joseph
Reply | Threaded
Open this post in threaded view
|

Re: How to place an Image inside an Image?

Thank you for your response Glenn.

Can you help me understand how to use DrapeableNode to achieve stenciling or placing an image inside an image?  DrapeableNode appears to simply have a method for setting mapNode and not much else.

Many thanks in advance!

// joe
gwaldron gwaldron
Reply | Threaded
Open this post in threaded view
|

Re: How to place an Image inside an Image?

What is your final objective? i.e. Why are you trying to do this? Some context will help answer your question. Thanks.
Glenn Waldron / Pelican Mapping
joseph joseph
Reply | Threaded
Open this post in threaded view
|

Re: How to place an Image inside an Image?

Hello again Glenn!

I have a smaller image whose color is blending with the color of the larger image behind it.
In my image attached, you can see 2 shades of red - the green color should of the outer image should not be influencing the red color of the inner image.



Here is my current code:
------------------------------------------------------------------------------------------------------------------

    osgEarth::MapNode* mapNode = inMapNode; // passed in by method argument
    osg::Group* _rootNode = inRootNode; // passed in by method argument
    osg::Group* _viewGroup = inViewGroup; // passed in by method argument

    osg::ref_ptr<osg::Group> group_overlay = new osg::Group();
    osg::ref_ptr<osg::Group> group_outer = new osg::Group();
    osg::ref_ptr<osg::Group> group_inner = new osg::Group();

    _rootNode->addChild(group_overlay);
    group_overlay->addChild(group_outer);
    group_overlay->addChild(group_inner);

    // make sure that the global color mask exists.
    osg::ColorMask* rootColorMask = new osg::ColorMask;
    rootColorMask->setMask(true,true,true,true);

    // set up depth to be inherited by the rest of the scene unless
    // overridden. this is overridden in goup_inner.
    osg::Depth* rootDepth = new osg::Depth;
    rootDepth->setFunction(osg::Depth::LESS);
    rootDepth->setRange(0.0,1.0);

    osg::StateSet* rootStateSet = new osg::StateSet();
    rootStateSet->setAttribute(rootColorMask);
    rootStateSet->setAttribute(rootDepth);

    group_overlay->setStateSet(rootStateSet);

        ////////////////////////////////////////////////////////////////////////////////

    // Assign pseudo colored image
    osg::ref_ptr<osg::Image> result = new osg::Image;
    result->setImage(width, height, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, new_png, osg::Image::NO_DELETE);

    osg::ref_ptr<osgEarth::Annotation::ImageOverlay> innerImageOverlay = nullptr;
    innerImageOverlay = new osgEarth::Annotation::ImageOverlay( mapNode, result );
    innerImageOverlay->setBounds( osgEarth::Bounds( lon1_deg, lat1_deg, lon2_deg, lat2_deg) );
    innerImageOverlay->setDraped(false);

    // set up depth so all writing to depth goes to maximum depth.
    osg::Depth* depth = new osg::Depth;
    depth->setFunction(osg::Depth::ALWAYS);
    depth->setRange(1.0,1.0);

    // set up the stencil ops so that only operator on this mirrors stencil value.
    osg::Stencil* stencil = new osg::Stencil;
    stencil->setFunction(osg::Stencil::ALWAYS,1,~0u);
    stencil->setOperation(osg::Stencil::REPLACE, osg::Stencil::REPLACE, osg::Stencil::REPLACE);

    // set up additive blending.
    osg::BlendFunc* trans = new osg::BlendFunc;
    trans->setFunction(osg::BlendFunc::ONE,osg::BlendFunc::ONE);

    osg::StateSet* state = new osg::StateSet;
    state->setMode(GL_CULL_FACE,osg::StateAttribute::OFF|osg::StateAttribute::PROTECTED);

    state->setRenderBinDetails(107, "RenderBin");
    state->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
    state->setAttributeAndModes(stencil,osg::StateAttribute::ON);
    state->setAttributeAndModes(trans,osg::StateAttribute::ON);
    state->setAttribute(depth);

    group_inner->setStateSet(state);
    group_inner->addChild( innerImageOverlay );
       
        ///////////////////////////////////////////////////////////////////////////////
       
    // Convert png data to osg::Image
    std::string dataBuffer( (char*)outer_overlay.png, outer_overlay.png_size );
    std::istringstream inputStream(dataBuffer);
    std::string dbOptionsStr = "";
    osgDB::Options *dbOptions = new osgDB::Options( dbOptionsStr );
    osg::ref_ptr<osg::Image> result = osgEarth::ImageUtils::readStream( inputStream, dbOptions );
       
    osg::ref_ptr<osgEarth::Annotation::ImageOverlay> outerImageOverlay = nullptr;
    outerImageOverlay = new osgEarth::Annotation::ImageOverlay(mapNode, result);
    outerImageOverlay->setDraped(false);

    // set up depth so all writing to depth goes to maximum depth.
    osg::Depth* depth = new osg::Depth;
    depth->setFunction(osg::Depth::ALWAYS);

    osg::Stencil* stencil = new osg::Stencil;
    stencil->setFunction(osg::Stencil::NOTEQUAL,1,~0u);
    stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);

    osg::StateSet* state = new osg::StateSet;
    state->setMode(GL_CULL_FACE,osg::StateAttribute::OFF|osg::StateAttribute::PROTECTED);
    state->setRenderBinDetails(106,"RenderBin");
    state->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
    state->setAttributeAndModes(stencil,osg::StateAttribute::ON);
    state->setAttribute(depth);

    group_outer->setStateSet(state);
    group_outer->addChild( outerImageOverlay );
------------------------------------------------------------------------------------------------------------------

Thank you so much for your assistance :)



nmielcarek nmielcarek
Reply | Threaded
Open this post in threaded view
|

Re: How to place an Image inside an Image?

For completeness, here is how joseph and I have solved this problem using osgEarth ImageOverlays. This allows us to insert a semi-transparent image within another image without having them blend together, but still allowing the underlying terrain to see through.

main():
        osg::Group* root = new osg::Group();
        viewer.setSceneData( root );

        osg::StateSet* rootStateSet = root->getOrCreateStateSet();

        osg::ColorMask* rootColorMask = new osg::ColorMask;
        rootColorMask->setMask(true,true,true,true);
        rootStateSet->setAttribute(rootColorMask, osg::StateAttribute::ON);

        osg::Depth* rootDepth = new osg::Depth;
        rootDepth->setFunction(osg::Depth::LESS);
        rootDepth->setRange(0.0, 1.0);
        rootStateSet->setAttribute(rootDepth);

        // clear stencil buffer values
        osg::Stencil* stencil = new osg::Stencil;
        stencil->setFunction(osg::Stencil::ALWAYS, 0, ~0u);
        stencil->setOperation(osg::Stencil::REPLACE, osg::Stencil::REPLACE, osg::Stencil::REPLACE);
        rootStateSet->setAttributeAndModes(stencil, osg::StateAttribute::ON);

        viewer.getCamera()->setPostDrawCallback(new MyPostDrawCallback(mapNode));

//////////////

  struct MyPostDrawCallback : public osg::Camera::DrawCallback
  {
    MyPostDrawCallback(osgEarth::MapNode* mapNode)
        {
                // Read the initial image files
                osg::ref_ptr<osg::Image> image1 = osgDB::readRefImageFile("img1.png");
                osg::ref_ptr<osg::Image> image2 = osgDB::readRefImageFile("img2.png");

                // Create ImageOverlays and set initial bounds
                _overlay1 = new osgEarth::Annotation::ImageOverlay(mapNode);
                _overlay1->setImage( image1.get() );
                _overlay1->setBounds( osgEarth::Bounds(-78, 35, -72, 40) );
                _overlay1->setDraped(true);  // does draping support stencils?

                osg::StateSet* stateSet1 = _overlay1->getOrCreateStateSet();
                stateSet1->setAttribute(new osg::ColorMask(true, true, true, true), osg::StateAttribute::ON);
                stateSet1->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);

                osg::Stencil* stencil1 = new osg::Stencil;
                stencil1->setFunction(osg::Stencil::ALWAYS, 1, ~0u);
                stencil1->setOperation(osg::Stencil::REPLACE, osg::Stencil::REPLACE, osg::Stencil::REPLACE);
                stateSet1->setAttributeAndModes(stencil1, osg::StateAttribute::ON);

                osg::Depth* depth1 = new osg::Depth;
                depth1->setFunction(osg::Depth::ALWAYS);
                depth1->setRange(1.0, 1.0);
                stateSet1->setAttribute(depth1);

                mapNode->addChild( _overlay1 );

                _overlay2 = new osgEarth::Annotation::ImageOverlay(mapNode);
                _overlay2->setImage( image2.get() );
                _overlay2->setBounds( osgEarth::Bounds(-80, 32, -70, 43) );
                _overlay2->setDraped(false);  // does draping support stencils?

                osg::StateSet* stateSet2 = _overlay2->getOrCreateStateSet();
                stateSet2->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);

                // test stencil values to determine whether to draw or not
                osg::Stencil* stencil2 = new osg::Stencil;
                stencil2->setFunction(osg::Stencil::NOTEQUAL, 1, ~0u);
                stateSet2->setAttributeAndModes(stencil2, osg::StateAttribute::ON);

                osg::Depth* depth2 = new osg::Depth;
                depth2->setFunction(osg::Depth::ALWAYS);
                stateSet2->setAttribute(depth2);

                mapNode->addChild( _overlay2 );
        }
        virtual void operator () (const osg::Camera & /*camera*/) const
        {
                // assign new images (only if required)
                osg::ref_ptr<osg::Image> image1 = osgDB::readRefImageFile("img1.png");
                _overlay1->setImage( image1.get() );
                _overlay1->setBounds( osgEarth::Bounds(-103, 35, -95, 40) );
                _overlay1->setDraped(false);

                osg::ref_ptr<osg::Image> image2 = osgDB::readRefImageFile("img2.png");
                _overlay2->setImage( image2.get() );
                _overlay2->setBounds( osgEarth::Bounds(-106, 34, -92, 43) );
                _overlay2->setDraped(false);
        }
        osgEarth::Annotation::ImageOverlay* _overlay1;
        osgEarth::Annotation::ImageOverlay* _overlay2;
};


-Nathan