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

Revision 6043, 19.9 kB (checked in by robert, 7 years ago)

Added cube test model.

  • 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/FlightManipulator>
14#include <osgGA/DriveManipulator>
15#include <osgGA/KeySwitchMatrixManipulator>
16#include <osgGA/AnimationPathManipulator>
17#include <osgGA/TerrainManipulator>
18#include <osgGA/AnimationPathManipulator>
19#include <osgGA/StateSetManipulator>
20
21#include <osgViewer/Viewer>
22#include <osgViewer/StatsHandler>
23
24#include <osgShadow/OccluderGeometry>
25
26#include <osgDB/ReadFile>
27#include <osgDB/WriteFile>
28
29#include <iostream>
30
31class ComputeBoundingBoxVisitor : public osg::NodeVisitor
32{
33public:
34    ComputeBoundingBoxVisitor():
35        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
36    {
37    }
38   
39    virtual void reset()
40    {
41        _matrixStack.clear();
42        _bb.init();
43    }
44   
45    osg::BoundingBox& getBoundingBox() { return _bb; }
46
47    void getPolytope(osg::Polytope& polytope, float margin=0.1) const
48    {
49        float delta = _bb.radius()*margin;
50        polytope.add( osg::Plane(0.0, 0.0, 1.0, -(_bb.zMin()-delta)) );
51        polytope.add( osg::Plane(0.0, 0.0, -1.0, (_bb.zMax()+delta)) );
52
53        polytope.add( osg::Plane(1.0, 0.0, 0.0, -(_bb.xMin()-delta)) );
54        polytope.add( osg::Plane(-1.0, 0.0, 0.0, (_bb.xMax()+delta)) );
55
56        polytope.add( osg::Plane(0.0, 1.0, 0.0, -(_bb.yMin()-delta)) );
57        polytope.add( osg::Plane(0.0, -1.0, 0.0, (_bb.yMax()+delta)) );
58    }
59       
60    void getBase(osg::Polytope& polytope, float margin=0.1) const
61    {
62        float delta = _bb.radius()*margin;
63        polytope.add( osg::Plane(0.0, 0.0, 1.0, -(_bb.zMin()-delta)) );
64    }
65   
66    void apply(osg::Node& node)
67    {
68        traverse(node);
69    }
70   
71    void apply(osg::Transform& transform)
72    {
73        osg::Matrix matrix;
74        if (!_matrixStack.empty()) matrix = _matrixStack.back();
75       
76        transform.computeLocalToWorldMatrix(matrix,this);
77       
78        pushMatrix(matrix);
79       
80        traverse(transform);
81       
82        popMatrix();
83    }
84   
85    void apply(osg::Geode& geode)
86    {
87        for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
88        {
89            apply(geode.getDrawable(i));
90        }
91    }
92   
93    void pushMatrix(osg::Matrix& matrix)
94    {
95        _matrixStack.push_back(matrix);
96    }
97   
98    void popMatrix()
99    {
100        _matrixStack.pop_back();
101    }
102
103    void apply(osg::Drawable* drawable)
104    {
105        if (_matrixStack.empty()) _bb.expandBy(drawable->getBound());
106        else
107        {
108            osg::Matrix& matrix = _matrixStack.back();
109            const osg::BoundingBox& dbb = drawable->getBound();
110            if (dbb.valid())
111            {
112                _bb.expandBy(dbb.corner(0) * matrix);
113                _bb.expandBy(dbb.corner(1) * matrix);
114                _bb.expandBy(dbb.corner(2) * matrix);
115                _bb.expandBy(dbb.corner(3) * matrix);
116                _bb.expandBy(dbb.corner(4) * matrix);
117                _bb.expandBy(dbb.corner(5) * matrix);
118                _bb.expandBy(dbb.corner(6) * matrix);
119                _bb.expandBy(dbb.corner(7) * matrix);
120            }
121        }
122    }
123   
124protected:
125   
126    typedef std::vector<osg::Matrix> MatrixStack;
127
128    MatrixStack         _matrixStack;
129    osg::BoundingBox    _bb;
130};
131
132osg::Node* createTestModel()
133{
134    osg::Geode* geode = new osg::Geode;
135   
136    osg::Geometry* geometry = new osg::Geometry;
137    geode->addDrawable(geometry);
138   
139    osg::Vec3Array* vertices = new osg::Vec3Array;
140    geometry->setVertexArray(vertices);
141   
142    osg::Vec3Array* normals = new osg::Vec3Array;
143    geometry->setNormalArray(normals);
144    geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
145   
146    osg::Vec4Array* colours = new osg::Vec4Array;
147    geometry->setColorArray(colours);
148    geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
149    colours->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
150   
151   
152    osg::Vec3 origin(0.0f,0.0f,0.0f);
153    osg::Vec3 dx(2.0f,0.0f,0.0f);
154    osg::Vec3 dy(0.0f,1.0f,0.0f);
155    osg::Vec3 dz(0.0f,0.0f,1.0f);
156   
157    osg::Vec3 px(1.0f,0.0,0.0f);
158    osg::Vec3 nx(-1.0f,0.0,0.0f);
159    osg::Vec3 py(0.0f,1.0f,0.0f);
160    osg::Vec3 ny(0.0f,-1.0f,0.0f);
161    osg::Vec3 pz(0.0f,0.0f,1.0f);
162    osg::Vec3 nz(0.0f,0.0f,-1.0f);
163
164    // front face   
165    vertices->push_back(origin);
166    vertices->push_back(origin+dx);
167    vertices->push_back(origin+dx+dz);
168    vertices->push_back(origin+dz);
169    normals->push_back(ny);
170    normals->push_back(ny);
171    normals->push_back(ny);
172    normals->push_back(ny);
173   
174    // front face   
175    vertices->push_back(origin+dy);
176    vertices->push_back(origin+dy+dz);
177    vertices->push_back(origin+dy+dx+dz);
178    vertices->push_back(origin+dy+dx);
179    normals->push_back(py);
180    normals->push_back(py);
181    normals->push_back(py);
182    normals->push_back(py);
183   
184    // left face   
185    vertices->push_back(origin+dy);
186    vertices->push_back(origin);
187    vertices->push_back(origin+dz);
188    vertices->push_back(origin+dy+dz);
189    normals->push_back(nx);
190    normals->push_back(nx);
191    normals->push_back(nx);
192    normals->push_back(nx);
193
194    // right face   
195    vertices->push_back(origin+dx+dy);
196    vertices->push_back(origin+dx+dy+dz);
197    vertices->push_back(origin+dx+dz);
198    vertices->push_back(origin+dx);
199    normals->push_back(px);
200    normals->push_back(px);
201    normals->push_back(px);
202    normals->push_back(px);
203
204    // top face   
205    vertices->push_back(origin+dz);
206    vertices->push_back(origin+dz+dx);
207    vertices->push_back(origin+dz+dx+dy);
208    vertices->push_back(origin+dz+dy);
209    normals->push_back(pz);
210    normals->push_back(pz);
211    normals->push_back(pz);
212    normals->push_back(pz);
213
214    // bottom face   
215    vertices->push_back(origin);
216    vertices->push_back(origin+dy);
217    vertices->push_back(origin+dx+dy);
218    vertices->push_back(origin+dx);
219    normals->push_back(nz);
220    normals->push_back(nz);
221    normals->push_back(nz);
222    normals->push_back(nz);
223
224    geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, vertices->size()));
225
226    return geode;
227}
228
229int main(int argc, char** argv)
230{
231    // use an ArgumentParser object to manage the program arguments.
232    osg::ArgumentParser arguments(&argc, argv);
233
234    // set up the usage document, in case we need to print out how to use this program.
235    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName() + " is the example which demonstrates using of GL_ARB_shadow extension implemented in osg::Texture class");
236    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName());
237    arguments.getApplicationUsage()->addCommandLineOption("-h or --help", "Display this information");
238    arguments.getApplicationUsage()->addCommandLineOption("--positionalLight", "Use a positional light.");
239    arguments.getApplicationUsage()->addCommandLineOption("--directionalLight", "Use a direction light.");
240    arguments.getApplicationUsage()->addCommandLineOption("--addOccluderToScene", "Add the occluders geometry.");
241    arguments.getApplicationUsage()->addCommandLineOption("--noUpdate", "Disable the updating the of light source.");
242    arguments.getApplicationUsage()->addCommandLineOption("--base", "Add a base geometry to test shadows.");
243    arguments.getApplicationUsage()->addCommandLineOption("--noShadow", "Disable the shadows.");
244    arguments.getApplicationUsage()->addCommandLineOption("--two-sided", "Use two-sided stencil extension for shadow volumes.");
245    arguments.getApplicationUsage()->addCommandLineOption("--two-pass", "Use two-pass stencil for shadow volumes.");
246
247    // hint to tell viewer to request stencil buffer when setting up windows
248    osg::DisplaySettings::instance()->setMinimumNumStencilBits(8);
249
250    // construct the viewer.
251    osgViewer::Viewer viewer;
252
253    // if user request help write it out to cout.
254    if (arguments.read("-h") || arguments.read("--help"))
255    {
256        arguments.getApplicationUsage()->write(std::cout);
257        return 1;
258    }
259
260    bool postionalLight = false;
261    while (arguments.read("--positionalLight")) postionalLight = true;
262    while (arguments.read("--directionalLight")) postionalLight = false;
263
264    bool addOccluderToScene = false;
265    while (arguments.read("--addOccluderToScene")) addOccluderToScene = true;
266
267    bool updateLightPosition = true;
268    while (arguments.read("--noUpdate")) updateLightPosition = false;
269
270    bool createBase = false;
271    while (arguments.read("--base")) createBase = true;
272
273    bool doShadow = true;
274    while (arguments.read("--noShadow")) doShadow = false;
275   
276    int screenNum = -1;
277    while (arguments.read("--screen", screenNum)) viewer.setUpViewOnSingleScreen(screenNum);
278
279    osgShadow::ShadowVolumeGeometry::DrawMode drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED;
280    while (arguments.read("--two-sided")) drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED;
281    while (arguments.read("--two-pass")) drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_PASS;
282   
283
284    // set up the camera manipulators.
285    {
286        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
287
288        keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
289        keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
290        keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
291        keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() );
292
293        std::string pathfile;
294        char keyForAnimationPath = '5';
295        while (arguments.read("-p",pathfile))
296        {
297            osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile);
298            if (apm || !apm->valid())
299            {
300                unsigned int num = keyswitchManipulator->getNumMatrixManipulators();
301                keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm );
302                keyswitchManipulator->selectMatrixManipulator(num);
303                ++keyForAnimationPath;
304            }
305        }
306
307        viewer.setCameraManipulator( keyswitchManipulator.get() );
308    }
309
310
311    // add the state manipulator
312    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
313
314    // add stats
315    viewer.addEventHandler( new osgViewer::StatsHandler() );
316
317    // any option left unread are converted into errors to write out later.
318    arguments.reportRemainingOptionsAsUnrecognized();
319
320    // report any errors if they have occured when parsing the program aguments.
321    if (arguments.errors())
322    {
323      arguments.writeErrorMessages(std::cout);
324      return 1;
325    }
326
327
328    osg::ref_ptr<osg::Node> model = osgDB::readNodeFiles(arguments);
329    if (!model)
330    {
331        model = createTestModel();
332    }
333
334    // get the bounds of the model.   
335    ComputeBoundingBoxVisitor cbbv;
336    model->accept(cbbv);
337    osg::BoundingBox bb = cbbv.getBoundingBox();
338
339    if (createBase)
340    {
341        osg::ref_ptr<osg::Group> newGroup = new osg::Group;
342        newGroup->addChild(model.get());
343       
344        osg::Geode* geode = new osg::Geode;
345       
346        osg::Vec3 widthVec(bb.radius(), 0.0f, 0.0f);
347        osg::Vec3 depthVec(0.0f, bb.radius(), 0.0f);
348        osg::Vec3 centerBase( (bb.xMin()+bb.xMax())*0.5f, (bb.yMin()+bb.yMax())*0.5f, bb.zMin()-bb.radius()*0.1f );
349       
350        geode->addDrawable( osg::createTexturedQuadGeometry( centerBase-widthVec*1.5f-depthVec*1.5f,
351                                                             widthVec*3.0f, depthVec*3.0f) );
352        newGroup->addChild(geode);
353       
354        model = newGroup.get();
355    }
356
357    // get the bounds of the model.
358    cbbv.reset();
359    model->accept(cbbv);
360    bb = cbbv.getBoundingBox();
361   
362    osg::ref_ptr<osg::Group> group = new osg::Group;
363
364    // set up the occluder
365    osg::ref_ptr<osgShadow::OccluderGeometry> occluder = new osgShadow::OccluderGeometry;
366    occluder->computeOccluderGeometry(model.get());
367//    cbbv.getPolytope(occluder->getBoundingPolytope(),0.001);
368    cbbv.getBase(occluder->getBoundingPolytope(),0.001);
369
370    if (addOccluderToScene)
371    {
372        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
373        geode->addDrawable(occluder.get());
374        group->addChild(geode.get());
375    }
376   
377    osg::ref_ptr<osgShadow::ShadowVolumeGeometry> shadowVolume = new osgShadow::ShadowVolumeGeometry;
378
379    // shadowVolume->setUseDisplayList(!updateLightPosition);
380    shadowVolume->setUseDisplayList(false);
381
382    osg::Vec4 lightpos;
383   
384    if (postionalLight)
385    {
386        lightpos.set(bb.center().x(), bb.center().y(), bb.zMax() + bb.radius()  ,1.0f);
387    }
388    else
389    {
390        lightpos.set(0.5f,0.25f,0.8f,0.0f);
391    }
392
393
394
395    osg::ref_ptr<osg::Light> light = new osg::Light;
396
397    if (!doShadow)
398    {
399        group->addChild(model.get());
400
401        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
402        occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
403        geode->addDrawable(shadowVolume.get());
404        group->addChild(geode.get());
405
406        osg::StateSet* ss = geode->getOrCreateStateSet();
407        ss->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
408
409    }
410    else
411    {
412        osg::Vec4 ambient(0.2,0.2,0.2,1.0);
413        osg::Vec4 diffuse(0.8,0.8,0.8,1.0);
414        osg::Vec4 zero_colour(0.0,0.0,0.0,1.0);
415   
416        // first group, render the depth buffer + ambient light contribution
417        {
418
419            osg::Group* first_model_group = new osg::Group;
420            first_model_group->addChild(model.get());
421            group->addChild(first_model_group);
422
423            osg::StateSet* ss1 = first_model_group->getOrCreateStateSet();
424
425            osg::LightModel* lm1 = new osg::LightModel;
426            lm1->setAmbientIntensity(ambient);
427            ss1->setAttribute(lm1);
428
429            osg::Light* light1 = new osg::Light;
430            light1->setAmbient(ambient);
431            light1->setDiffuse(zero_colour);
432            light1->setPosition(lightpos);
433            ss1->setAttributeAndModes(light1, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
434            ss1->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
435       
436        }   
437   
438        // second group
439        {
440            // use a camera here just to implement a seperate post rendering stage.
441            osg::Camera* camera = new osg::Camera;
442            camera->setRenderOrder(osg::Camera::POST_RENDER);
443            camera->setClearMask(GL_STENCIL_BUFFER_BIT);
444            group->addChild(camera);
445
446            osg::StateSet* ss_camera = camera->getOrCreateStateSet();
447
448            osg::Depth* depth = new osg::Depth;
449            depth->setWriteMask(false);
450            depth->setFunction(osg::Depth::LEQUAL);
451            ss_camera->setAttribute(depth);
452
453            {
454                osg::ref_ptr<osg::Geode> geode = new osg::Geode;
455                occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
456                shadowVolume->setDrawMode(drawMode);
457               
458
459                if (drawMode == osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED)
460                {
461                    osg::notify(osg::NOTICE)<<"STENCIL_TWO_SIDED seleteced"<<std::endl;
462
463                    osg::StencilTwoSided* stencil = new osg::StencilTwoSided;
464                    stencil->setFunction(osg::StencilTwoSided::BACK, osg::StencilTwoSided::ALWAYS,0,~0u);
465                    stencil->setOperation(osg::StencilTwoSided::BACK, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::DECR_WRAP);
466                    stencil->setFunction(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::ALWAYS,0,~0u);
467                    stencil->setOperation(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::INCR_WRAP);
468
469
470                    osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false);
471
472                    osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
473                    ss_sv1->setRenderBinDetails(0, "RenderBin");
474                    ss_sv1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
475                    ss_sv1->setAttribute(colourMask);
476                    ss_sv1->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
477                   
478                    geode->addDrawable(shadowVolume.get());
479                   
480                    camera->addChild(geode.get());
481
482                }
483                else
484                {
485                    osg::notify(osg::NOTICE)<<"STENCIL_TWO_PASSES seleteced"<<std::endl;
486
487                    osg::Stencil* stencil = new osg::Stencil;
488                    stencil->setFunction(osg::Stencil::ALWAYS,0,~0u);
489                    stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
490
491                    osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false);
492
493                    osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
494                    ss_sv1->setRenderBinDetails(0, "RenderBin");
495                    ss_sv1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
496                    ss_sv1->setAttribute(colourMask);
497                    ss_sv1->setMode(GL_CULL_FACE,osg::StateAttribute::ON);
498                   
499                    geode->addDrawable(shadowVolume.get());
500                   
501                    camera->addChild(geode.get());
502                }
503
504            }
505
506
507            // render scene graph adding contribution of light
508            {
509                osg::Group* second_model_group = new osg::Group;
510                second_model_group->addChild(model.get());
511
512
513                osg::StateSet* ss1 = second_model_group->getOrCreateStateSet();
514                ss1->setRenderBinDetails(5, "RenderBin");
515
516                osg::LightModel* lm1 = new osg::LightModel;
517                lm1->setAmbientIntensity(zero_colour);
518                ss1->setAttribute(lm1);
519
520
521                osg::LightSource* lightsource = new osg::LightSource;
522                lightsource->setLight(light.get());
523                light->setAmbient(zero_colour);
524                light->setDiffuse(diffuse);
525                light->setPosition(lightpos);
526                second_model_group->addChild(lightsource);
527
528                ss1->setMode(GL_LIGHT0, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
529
530                // set up the stencil ops so that only operator on this mirrors stencil value.
531                osg::Stencil* stencil = new osg::Stencil;
532                stencil->setFunction(osg::Stencil::EQUAL,0,~0u);
533                stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
534                ss1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
535
536                osg::BlendFunc* blend = new osg::BlendFunc;
537                blend->setFunction(osg::BlendFunc::ONE, osg::BlendFunc::ONE);
538                ss1->setAttributeAndModes(blend, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
539                ss1->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
540
541                camera->addChild(second_model_group);
542            }
543                   
544        }
545
546    }
547
548
549    viewer.setSceneData(group.get());
550   
551    // create the windows and run the threads.
552    viewer.realize();
553
554    // osgDB::writeNodeFile(*group,"test.osg");
555
556    while (!viewer.done())
557    {
558        if (updateLightPosition)
559        {
560            float t = viewer.getFrameStamp()->getReferenceTime();
561            if (postionalLight)
562            {
563                lightpos.set(bb.center().x()+sinf(t)*bb.radius(), bb.center().y() + cosf(t)*bb.radius(), bb.zMax() + bb.radius()  ,1.0f);
564            }
565            else
566            {
567                lightpos.set(sinf(t),cosf(t),0.8f,0.0f);
568            }
569            light->setPosition(lightpos);
570            occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
571        }
572
573        viewer.frame();
574    }
575   
576    return 0;
577}
Note: See TracBrowser for help on using the browser.