Version 3 (modified by osg, 7 years ago)

link to gpu gems 2

osgShadow

The osgShadow nodekit is a powerful collection of classes for adding shadows to your scene. Thanks to this powerful nodekit, you don't need to learn the complex calculations, multi-texturing units and programmable shaders involved in creating shadows. Just add these nodes to your scene graph, and the work is done for you.

The Class documentation for osgShadow shows the classes and methods. This document describes how to use them.

Simple Case

In the simplest case, there is a single light enabled in your scene. You create a ShadowedScene? node (a subclass of osg::Group), and add children to it. A ShadowTechnique? is chosen and assigned to the ShadowedScene. The children of ShadowedScene can have the CastShadow? bit set in their node mask, and/or the ReceivesShadow? bit. As the scene is rendered, ShadowedScene calls the methods of its ShadowTechnique to compute the shadows and decorate the scene graph with StateSets to render them:

http://www.openscenegraph.org/projects/osg/attachment/wiki/Support/ProgrammingGuide/osgShadow/shadows1.png?format=raw

Texture Resolution

Generally speaking, a projected texture is used to render the shadow. This means that if your shadow casting geometry is very large, then the texture resolution must be spread over a large area. This can produce blocky aliasing in the shadow. Therefore, it is advisable to keep the bounding sphere of your shadow casting geometry as small as possible. Just placing a ShadowedScene above a large scene graph, where everything casts a shadow, is not likely to produce good results. You should very deliberately choose which nodes will cast shadows.

You can also increase the resolution of the texture with the ShadowTechnique?'s setTextureResolution method.

The Techniques

As of OSG 2.4, there are five different shadow techniques available. All of them have varying requirements on the capabilities of your 3D card. If your card's shader engine and driver does not support certain commands, then it will not be capable of using some of these techniques.

  • ShadowMap?. This is the simplest option and most mature and stable implementation. TODO: how does it work, what are pros and cons of using it?
  • ShadowVolume?. As of OSG 2.4, this is still experimental and may not produce shadows on some graphics cards.
  • SoftShadowMap?.
    • As of OSG 2.4, this is still experimental and may not produce shadows on some graphics cards.
    • The original idea for this technique was published in GPU Gems 2 presumably the article "Efficient Soft-Edged Shadows Using Pixel Shader Branching".
  • ParallelSplitShadowMap?. This is the most complex and potentially powerful algorithm, with many more options that can be set.
    • PolygonOffset, MaxFarDistance, MoveVCamBehindRCamFactor, MinNearDistanceForSplits, forceFrontCullFace, useLinearSplit : need explanations for each of these settings.

Example Code

This code loads two cessnas, offset from each other. The first cessna casts a shadow on the second. In fact, because ShadowMap? treats everything as shadowed, the first cessna also self-shadows.

        const int ReceivesShadowTraversalMask = 0x1;
        const int CastsShadowTraversalMask = 0x2;

        osg::ref_ptr<osgShadow::ShadowedScene> shadowedScene = new osgShadow::ShadowedScene;

        shadowedScene->setReceivesShadowTraversalMask(ReceivesShadowTraversalMask);
        shadowedScene->setCastsShadowTraversalMask(CastsShadowTraversalMask);

        osg::ref_ptr<osgShadow::ShadowMap> sm = new osgShadow::ShadowMap;
        shadowedScene->setShadowTechnique(sm.get());

        int mapres = 1024;
        sm->setTextureSize(osg::Vec2s(mapres,mapres));

        osg::Group* cessna1 = (osg::Group*) osgDB::readNodeFile("cessna.osg");
        cessna1->setNodeMask(CastsShadowTraversalMask);
        cessna1->getChild(0)->setNodeMask(CastsShadowTraversalMask);

        osg::Group* cessna2 = (osg::Group*) osgDB::readNodeFile("cessna.osg");
        if (!cessna2)
                return;
        cessna2->setNodeMask(ReceivesShadowTraversalMask);
        cessna2->getChild(0)->setNodeMask(ReceivesShadowTraversalMask);

        osg::MatrixTransform* positioned = new osg::MatrixTransform;
        positioned->setMatrix(osg::Matrix::translate(40,0,0));
        positioned->addChild(cessna1);

        shadowedScene->addChild(positioned);
        shadowedScene->addChild(cessna2);

CastsShadow and ReceivesShadow

The two ShadowedScene masks are there to help the ShadowTechnique implementations differentiate where appropriate between different types of objects in the scene - with some techniques like ShadowTexture this is essential, with others it doesn't make so much sense and actually can be rather awkward to implement. osgShadow library itself is also still quite young with the various implementations still not fully ground out, so some areas that they don't currently implement fully will hopefully be filled out in the future.

For example, with OSG 2.4, ShadowMap respects the CastsShadow bit (only objects with that bit will casts a shadow) but ignores the ReceivesShadow bit (all objects in the shadow scene graph receive shadows).

Common Questions

  • Where in the scene graph does ShadowedScene go? Does it have to be at the root?
    • You can put your ShadowedScene node anywhere in the scene graph.
  • What if the shadower and shadowed nodes are far apart in the scene graph, should the ShadowedScene be inserted above their mutual parent?
    • Yes. The root node of the subgraph which you want to have shadows should be an osgShadow::ShadowedScene
  • Do the ReceivesShadow and CastsShadow mask bits need to be set to 0 for all the other nodes in the tree under ShadowedScene, to omit them from the shadow computation?
    • Yes. You will have to iterate through the entire scene graph to change the node mask for every node from its default (0xffffffff) to turn those bits off (generally 0xfffffffc). However, remember that some shadow techniques will ignore some bits in some cases, so you may not be able to omit nodes in all cases.
  • Does the Light need to be present in the graph under the ShadowedScene?
    • No, you don't need to add light to shadowed scene nor it does not need to be present in fixed location in viewer scene hierarchy. In case of many lights, it would be helpful if you point out the Light source that must be used to generate shadows. See !ShadowMap::setLight function.
  • Does ShadowedScene always rerender the shadow every frame?
    • Yes.
  • If I know that the light and geometry are not moving, can I avoid the shadows being constantly re-rendered?
  • What if my objects already have a shader applied to them?
    • ?
  • Can osgShadow be combines with osg::Fog?
    • Probably not (?) Most of the ShadowTechniques use shaders. osg::Fog wraps up glFog, which is part of OpenGL's fixed function pipeline. As soon as you enable shaders you loose fog support unless you shaders directly include a fog shader in them.

Example osgshadow

The OSG examples include an application called osgshadow, which gives a very simple demonstration of how to call the osgShadow nodekit.

Attachments