root/OpenSceneGraph/trunk/examples/osgshadow/osgshadow.cpp @ 6010

Revision 6010, 15.1 kB (checked in by robert, 8 years ago)

Added osg::StencilTwoSided? state attribute to wrap up the stencil_two_sided extension.

Added path in osgShadow work for using StencilTwoSided? to accelerate shadow volume rendering.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osg/ArgumentParser>
2
3#include <osg/LightModel>
4#include <osg/Depth>
5#include <osg/BlendFunc>
6#include <osg/Camera>
7#include <osg/Stencil>
8#include <osg/StencilTwoSided>
9#include <osg/CullFace>
10#include <osg/Geometry>
11
12#include <osgGA/TrackballManipulator>
13#include <osgGA/AnimationPathManipulator>
14
15#include <osgViewer/Viewer>
16
17#include <osgShadow/OccluderGeometry>
18
19#include <osgDB/ReadFile>
20#include <osgDB/WriteFile>
21
22#include <iostream>
23
24class ComputeBoundingBoxVisitor : public osg::NodeVisitor
25{
26public:
27    ComputeBoundingBoxVisitor():
28        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
29    {
30    }
31   
32    virtual void reset()
33    {
34        _matrixStack.clear();
35        _bb.init();
36    }
37   
38    osg::BoundingBox& getBoundingBox() { return _bb; }
39
40    void getPolytope(osg::Polytope& polytope, float margin=0.1) const
41    {
42        float delta = _bb.radius()*margin;
43        polytope.add( osg::Plane(0.0, 0.0, 1.0, -(_bb.zMin()-delta)) );
44        polytope.add( osg::Plane(0.0, 0.0, -1.0, (_bb.zMax()+delta)) );
45
46        polytope.add( osg::Plane(1.0, 0.0, 0.0, -(_bb.xMin()-delta)) );
47        polytope.add( osg::Plane(-1.0, 0.0, 0.0, (_bb.xMax()+delta)) );
48
49        polytope.add( osg::Plane(0.0, 1.0, 0.0, -(_bb.yMin()-delta)) );
50        polytope.add( osg::Plane(0.0, -1.0, 0.0, (_bb.yMax()+delta)) );
51    }
52       
53    void getBase(osg::Polytope& polytope, float margin=0.1) const
54    {
55        float delta = _bb.radius()*margin;
56        polytope.add( osg::Plane(0.0, 0.0, 1.0, -(_bb.zMin()-delta)) );
57    }
58   
59    void apply(osg::Node& node)
60    {
61        traverse(node);
62    }
63   
64    void apply(osg::Transform& transform)
65    {
66        osg::Matrix matrix;
67        if (!_matrixStack.empty()) matrix = _matrixStack.back();
68       
69        transform.computeLocalToWorldMatrix(matrix,this);
70       
71        pushMatrix(matrix);
72       
73        traverse(transform);
74       
75        popMatrix();
76    }
77   
78    void apply(osg::Geode& geode)
79    {
80        for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
81        {
82            apply(geode.getDrawable(i));
83        }
84    }
85   
86    void pushMatrix(osg::Matrix& matrix)
87    {
88        _matrixStack.push_back(matrix);
89    }
90   
91    void popMatrix()
92    {
93        _matrixStack.pop_back();
94    }
95
96    void apply(osg::Drawable* drawable)
97    {
98        if (_matrixStack.empty()) _bb.expandBy(drawable->getBound());
99        else
100        {
101            osg::Matrix& matrix = _matrixStack.back();
102            const osg::BoundingBox& dbb = drawable->getBound();
103            if (dbb.valid())
104            {
105                _bb.expandBy(dbb.corner(0) * matrix);
106                _bb.expandBy(dbb.corner(1) * matrix);
107                _bb.expandBy(dbb.corner(2) * matrix);
108                _bb.expandBy(dbb.corner(3) * matrix);
109                _bb.expandBy(dbb.corner(4) * matrix);
110                _bb.expandBy(dbb.corner(5) * matrix);
111                _bb.expandBy(dbb.corner(6) * matrix);
112                _bb.expandBy(dbb.corner(7) * matrix);
113            }
114        }
115    }
116   
117protected:
118   
119    typedef std::vector<osg::Matrix> MatrixStack;
120
121    MatrixStack         _matrixStack;
122    osg::BoundingBox    _bb;
123};
124
125
126
127int main(int argc, char** argv)
128{
129    // use an ArgumentParser object to manage the program arguments.
130    osg::ArgumentParser arguments(&argc, argv);
131
132    // set up the usage document, in case we need to print out how to use this program.
133    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName() + " is the example which demonstrates using of GL_ARB_shadow extension implemented in osg::Texture class");
134    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName());
135    arguments.getApplicationUsage()->addCommandLineOption("-h or --help", "Display this information");
136    arguments.getApplicationUsage()->addCommandLineOption("--with-base-texture", "Adde base texture to shadowed model.");
137    arguments.getApplicationUsage()->addCommandLineOption("--no-base-texture", "Adde base texture to shadowed model.");
138
139    // construct the viewer.
140    osgViewer::Viewer viewer;
141
142    // if user request help write it out to cout.
143    if (arguments.read("-h") || arguments.read("--help"))
144    {
145        arguments.getApplicationUsage()->write(std::cout);
146        return 1;
147    }
148
149    bool postionalLight = false;
150    while (arguments.read("--positionalLight")) postionalLight = true;
151    while (arguments.read("--directionalLight")) postionalLight = false;
152
153    bool addOccluderToScene = false;
154    while (arguments.read("--addOccluderToScene")) addOccluderToScene = true;
155
156    bool updateLightPosition = true;
157    while (arguments.read("--noUpdate")) updateLightPosition = false;
158
159    bool createBase = false;
160    while (arguments.read("--base")) createBase = true;
161
162    bool doShadow = true;
163    while (arguments.read("--noShadow")) doShadow = false;
164   
165    osgShadow::ShadowVolumeGeometry::DrawMode drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_PASS;
166    while (arguments.read("--two-sided")) drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED;
167   
168
169    std::string pathfile;
170    while (arguments.read("-p",pathfile))
171    {
172        osg::ref_ptr<osgGA::AnimationPathManipulator> apm = new osgGA::AnimationPathManipulator(pathfile);
173        if (apm->valid()) viewer.setCameraManipulator(apm.get());
174    }
175
176    if (!viewer.getCameraManipulator()) viewer.setCameraManipulator( new osgGA::TrackballManipulator() );
177
178    // any option left unread are converted into errors to write out later.
179    arguments.reportRemainingOptionsAsUnrecognized();
180
181    // report any errors if they have occured when parsing the program aguments.
182    if (arguments.errors())
183    {
184      arguments.writeErrorMessages(std::cout);
185      return 1;
186    }
187
188
189    osg::ref_ptr<osg::Node> model = osgDB::readNodeFiles(arguments);
190    if (!model)
191    {
192        osg::notify(osg::NOTICE)<<"No model loaded, please specify a model to load."<<std::endl;
193        return 1;
194    }
195
196    // get the bounds of the model.   
197    ComputeBoundingBoxVisitor cbbv;
198    model->accept(cbbv);
199    osg::BoundingBox bb = cbbv.getBoundingBox();
200
201    if (createBase)
202    {
203        osg::ref_ptr<osg::Group> newGroup = new osg::Group;
204        newGroup->addChild(model.get());
205       
206        osg::Geode* geode = new osg::Geode;
207       
208        osg::Vec3 widthVec(bb.radius(), 0.0f, 0.0f);
209        osg::Vec3 depthVec(0.0f, bb.radius(), 0.0f);
210        osg::Vec3 centerBase( (bb.xMin()+bb.xMax())*0.5f, (bb.yMin()+bb.yMax())*0.5f, bb.zMin()-bb.radius()*0.1f );
211       
212        geode->addDrawable( osg::createTexturedQuadGeometry( centerBase-widthVec*1.5f-depthVec*1.5f,
213                                                             widthVec*3.0f, depthVec*3.0f) );
214        newGroup->addChild(geode);
215       
216        model = newGroup.get();
217    }
218
219    // get the bounds of the model.
220    cbbv.reset();
221    model->accept(cbbv);
222    bb = cbbv.getBoundingBox();
223   
224    osg::ref_ptr<osg::Group> group = new osg::Group;
225
226    // set up the occluder
227    osg::ref_ptr<osgShadow::OccluderGeometry> occluder = new osgShadow::OccluderGeometry;
228    occluder->computeOccluderGeometry(model.get());
229//    cbbv.getPolytope(occluder->getBoundingPolytope(),0.001);
230    cbbv.getBase(occluder->getBoundingPolytope(),0.001);
231
232    if (addOccluderToScene)
233    {
234        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
235        geode->addDrawable(occluder.get());
236        group->addChild(geode.get());
237    }
238   
239    osg::ref_ptr<osgShadow::ShadowVolumeGeometry> shadowVolume = new osgShadow::ShadowVolumeGeometry;
240
241    // shadowVolume->setUseDisplayList(!updateLightPosition);
242    shadowVolume->setUseDisplayList(false);
243
244    osg::Vec4 lightpos;
245   
246    if (postionalLight)
247    {
248        lightpos.set(bb.center().x(), bb.center().y(), bb.zMax() + bb.radius()  ,1.0f);
249    }
250    else
251    {
252        lightpos.set(0.5f,0.25f,0.8f,0.0f);
253    }
254
255
256
257    osg::ref_ptr<osg::Light> light = new osg::Light;
258
259    if (!doShadow)
260    {
261        group->addChild(model.get());
262
263        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
264        occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
265        geode->addDrawable(shadowVolume.get());
266        group->addChild(geode.get());
267
268        osg::StateSet* ss = geode->getOrCreateStateSet();
269        ss->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
270
271    }
272    else
273    {
274        osg::Vec4 ambient(0.2,0.2,0.2,1.0);
275        osg::Vec4 diffuse(0.8,0.8,0.8,1.0);
276        osg::Vec4 zero_colour(0.0,0.0,0.0,1.0);
277   
278        // first group, render the depth buffer + ambient light contribution
279        {
280
281            osg::Group* first_model_group = new osg::Group;
282            first_model_group->addChild(model.get());
283            group->addChild(first_model_group);
284
285            osg::StateSet* ss1 = first_model_group->getOrCreateStateSet();
286
287            osg::LightModel* lm1 = new osg::LightModel;
288            lm1->setAmbientIntensity(ambient);
289            ss1->setAttribute(lm1);
290
291            osg::Light* light1 = new osg::Light;
292            light1->setAmbient(ambient);
293            light1->setDiffuse(zero_colour);
294            light1->setPosition(lightpos);
295            ss1->setAttributeAndModes(light1, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
296            ss1->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
297       
298        }   
299   
300        // second group
301        {
302            // use a camera here just to implement a seperate post rendering stage.
303            osg::Camera* camera = new osg::Camera;
304            camera->setRenderOrder(osg::Camera::POST_RENDER);
305            camera->setClearMask(GL_STENCIL_BUFFER_BIT);
306            group->addChild(camera);
307
308            osg::StateSet* ss_camera = camera->getOrCreateStateSet();
309
310            osg::Depth* depth = new osg::Depth;
311            depth->setWriteMask(false);
312            depth->setFunction(osg::Depth::LEQUAL);
313            ss_camera->setAttribute(depth);
314
315            {
316                osg::ref_ptr<osg::Geode> geode = new osg::Geode;
317                occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
318                shadowVolume->setDrawMode(drawMode);
319               
320
321                if (drawMode == osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED)
322                {
323                    osg::notify(osg::NOTICE)<<"STENCIL_TWO_SIDED seleteced"<<std::endl;
324
325                    osg::StencilTwoSided* stencil = new osg::StencilTwoSided;
326                    stencil->setFunction(osg::StencilTwoSided::BACK, osg::StencilTwoSided::ALWAYS,0,~0u);
327                    stencil->setOperation(osg::StencilTwoSided::BACK, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::DECR_WRAP);
328                    stencil->setFunction(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::ALWAYS,0,~0u);
329                    stencil->setOperation(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::INCR_WRAP);
330
331
332                    osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false);
333
334                    osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
335                    ss_sv1->setRenderBinDetails(0, "RenderBin");
336                    ss_sv1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
337                    ss_sv1->setAttribute(colourMask);
338                    ss_sv1->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
339                   
340                    geode->addDrawable(shadowVolume.get());
341                   
342                    camera->addChild(geode.get());
343
344                }
345                else
346                {
347                    osg::notify(osg::NOTICE)<<"STENCIL_TWO_PASSES seleteced"<<std::endl;
348
349                    osg::Stencil* stencil = new osg::Stencil;
350                    stencil->setFunction(osg::Stencil::ALWAYS,0,~0u);
351                    stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
352
353                    osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false);
354
355                    osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
356                    ss_sv1->setRenderBinDetails(0, "RenderBin");
357                    ss_sv1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
358                    ss_sv1->setAttribute(colourMask);
359                    ss_sv1->setMode(GL_CULL_FACE,osg::StateAttribute::ON);
360                   
361                    geode->addDrawable(shadowVolume.get());
362                   
363                    camera->addChild(geode.get());
364                }
365
366            }
367
368
369            // render scene graph adding contribution of light
370            {
371                osg::Group* second_model_group = new osg::Group;
372                second_model_group->addChild(model.get());
373
374
375                osg::StateSet* ss1 = second_model_group->getOrCreateStateSet();
376                ss1->setRenderBinDetails(5, "RenderBin");
377
378                osg::LightModel* lm1 = new osg::LightModel;
379                lm1->setAmbientIntensity(zero_colour);
380                ss1->setAttribute(lm1);
381
382
383                osg::LightSource* lightsource = new osg::LightSource;
384                lightsource->setLight(light.get());
385                light->setAmbient(zero_colour);
386                light->setDiffuse(diffuse);
387                light->setPosition(lightpos);
388                second_model_group->addChild(lightsource);
389
390                ss1->setMode(GL_LIGHT0, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
391
392                // set up the stencil ops so that only operator on this mirrors stencil value.
393                osg::Stencil* stencil = new osg::Stencil;
394                stencil->setFunction(osg::Stencil::EQUAL,0,~0u);
395                stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
396                ss1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
397
398                osg::BlendFunc* blend = new osg::BlendFunc;
399                blend->setFunction(osg::BlendFunc::ONE, osg::BlendFunc::ONE);
400                ss1->setAttributeAndModes(blend, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
401                ss1->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
402
403                camera->addChild(second_model_group);
404            }
405                   
406        }   
407
408    }
409
410    // hint to tell viewer to request stencil buffer when setting up windows
411    osg::DisplaySettings::instance()->setMinimumNumStencilBits(8);
412
413    viewer.setSceneData(group.get());
414
415    // create the windows and run the threads.
416    viewer.realize();
417
418    osgDB::writeNodeFile(*group,"test.osg");
419
420    while (!viewer.done())
421    {
422        if (updateLightPosition)
423        {
424            float t = viewer.getFrameStamp()->getReferenceTime();
425            if (postionalLight)
426            {
427                lightpos.set(bb.center().x()+sinf(t)*bb.radius(), bb.center().y() + cosf(t)*bb.radius(), bb.zMax() + bb.radius()  ,1.0f);
428            }
429            else
430            {
431                lightpos.set(sinf(t),cosf(t),0.8f,0.0f);
432            }
433            light->setPosition(lightpos);
434            occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
435        }
436
437        viewer.frame();
438    }
439   
440    return 0;
441}
Note: See TracBrowser for help on using the browser.