root/OpenSceneGraph/trunk/examples/osgoit/osgoit.cpp @ 11675

Revision 11675, 18.0 kB (checked in by robert, 4 years ago)

From Mathias Froehlich, "I have now put together what I have for the order independent transparency or
short oit. This rendering technique is also known as depth peeling.

Attached is the example that makes depth peeling work with the fixed function
pipeline. Ok, this is 'old fashioned' but required for our use case that
still has to work on older UNIX OpenGL implementations as well as together
with a whole existing application making use of the fixed function pipeline.
I can imagine to add support for shaders when we have that shader composition
framework where we can add a second depth test in a generic way.

This does *not* implement the dual depth peeling described in a paper from the
ETH Zurich.

This example could serve as a test case for the feature that you can on the
fly remove pre render cameras that you made work a few time ago.
It is also a test case for the new TraversalOrderBin? that is used to composite
the depth layers in the correct blend order.
This example also stresses your new texture object cache since you can change
some parameters for the oit implementation at runtime.

You can just load any model with osgoit and see how it works.
Use the usual help key to see what you can change.

There is already an osgdepthpeeling example that I could not really make sense
of up to now. So I just made something new without touching what I do not
understand."

Line 
1#include <osg/Array>
2#include <osg/AlphaFunc>
3#include <osg/BlendFunc>
4#include <osg/Depth>
5#include <osg/Geode>
6#include <osg/Geometry>
7#include <osg/Vec3>
8#include <osg/MatrixTransform>
9#include <osg/Texture2D>
10#include <osg/TextureRectangle>
11#include <osg/TexGen>
12#include <osg/TexEnv>
13#include <osg/TexMat>
14#include <osg/TexGenNode>
15
16#include <osgDB/ReadFile>
17
18#include <osgViewer/Viewer>
19#include <osgViewer/ViewerEventHandlers>
20
21#include <osg/Math>
22
23#include <limits>
24#include <iostream>
25
26// Some choices for the kind of textures we can use ...
27#define USE_TEXTURE_RECTANGLE
28// #define USE_NON_POWER_OF_TWO_TEXTURE
29#define USE_PACKED_DEPTH_STENCIL
30
31template<typename T>
32inline T
33nextPowerOfTwo(T k)
34{
35    if (k == T(0))
36        return 1;
37    k--;
38    for (int i = 1; i < std::numeric_limits<T>::digits; i <<= 1)
39        k = k | k >> i;
40    return k + 1;
41}
42
43class DepthPeeling : public osg::Referenced {
44public:
45    osg::Node*
46    createQuad(unsigned layerNumber, unsigned numTiles)
47    {
48        float tileSpan = 1;
49        float tileOffsetX = 0;
50        float tileOffsetY = 0;
51        if (_showAllLayers) {
52            tileSpan /= numTiles;
53            tileOffsetX = tileSpan * (layerNumber%numTiles);
54            tileOffsetY = 1 - tileSpan * (1 + layerNumber/numTiles);
55        }
56
57        osg::Vec3Array* vertices = new osg::Vec3Array;
58
59        vertices->push_back(osg::Vec3f(tileOffsetX           , tileOffsetY            , 0));
60        vertices->push_back(osg::Vec3f(tileOffsetX           , tileOffsetY  + tileSpan, 0));
61        vertices->push_back(osg::Vec3f(tileOffsetX + tileSpan, tileOffsetY  + tileSpan, 0));
62        vertices->push_back(osg::Vec3f(tileOffsetX + tileSpan, tileOffsetY            , 0));
63
64        osg::Vec3Array* colors = new osg::Vec3Array;
65        colors->push_back(osg::Vec3(1, 1, 1));
66     
67        osg::Vec2Array* texcoords = new osg::Vec2Array;
68        texcoords->push_back(osg::Vec2f(0, 0));
69        texcoords->push_back(osg::Vec2f(0, 1));
70        texcoords->push_back(osg::Vec2f(1, 1));
71        texcoords->push_back(osg::Vec2f(1, 0));
72     
73        osg::Geometry* geometry = new osg::Geometry;
74        geometry->setVertexArray(vertices);
75        geometry->setTexCoordArray(0, texcoords);
76     
77        geometry->setColorArray(colors);
78        geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
79     
80        geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
81     
82        osg::Geode* geode = new osg::Geode;
83        geode->addDrawable(geometry);
84     
85        return geode;
86    }
87   
88    class CullCallback : public osg::NodeCallback {
89    public:
90        CullCallback(unsigned texUnit, unsigned texWidth, unsigned texHeight, unsigned offsetValue) :
91            _texUnit(texUnit),
92            _texWidth(texWidth),
93            _texHeight(texHeight),
94            _offsetValue(offsetValue)
95        {
96        }
97     
98        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
99        {
100            osgUtil::CullVisitor* cullVisitor = static_cast<osgUtil::CullVisitor*>(nv);
101            osgUtil::RenderStage* renderStage = cullVisitor->getCurrentRenderStage();
102            const osg::Viewport* viewport = renderStage->getViewport();
103
104            osg::Matrixd m(*cullVisitor->getProjectionMatrix());
105            m.postMultTranslate(osg::Vec3d(1, 1, 1));
106            m.postMultScale(osg::Vec3d(0.5, 0.5, 0.5));
107
108            // scale the texture coordinates to the viewport
109#ifdef USE_TEXTURE_RECTANGLE
110            m.postMultScale(osg::Vec3d(viewport->width(), viewport->height(), 1));
111#else
112#ifndef USE_NON_POWER_OF_TWO_TEXTURE
113            m.postMultScale(osg::Vec3d(viewport->width()/double(_texWidth), viewport->height()/double(_texHeight), 1));
114#endif
115#endif
116
117            // Kind of polygon offset: note this way, we can also offset lines and points.
118            // Whereas with the polygon offset we could only handle surface primitives.
119            m.postMultTranslate(osg::Vec3d(0, 0, -ldexp(double(_offsetValue), -24)));
120
121            osg::TexMat* texMat = new osg::TexMat(m);
122            osg::StateSet* stateSet = new osg::StateSet;
123            stateSet->setTextureAttribute(_texUnit, texMat);
124            cullVisitor->pushStateSet(stateSet);
125            traverse(node, nv);
126            cullVisitor->popStateSet();
127        }
128
129    private:
130        unsigned _texUnit;
131        unsigned _texWidth;
132        unsigned _texHeight;
133        unsigned _offsetValue;
134    };
135
136    void
137    createPeeling()
138    {
139        int numTiles = ceil(sqrt(double(_numPasses)));
140
141        _root->removeChildren(0, _root->getNumChildren());
142        _colorTextures.clear();
143
144        // If not enabled, just use the top level camera
145        if (!_depthPeelingEnabled) {
146            _root->addChild(_scene.get());
147            return;
148        }
149
150        _compositeCamera = new osg::Camera;
151        _compositeCamera->setDataVariance(osg::Object::DYNAMIC);
152        _compositeCamera->setInheritanceMask(osg::Camera::READ_BUFFER | osg::Camera::DRAW_BUFFER);
153        _compositeCamera->setRenderOrder(osg::Camera::POST_RENDER);
154        _compositeCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_PRIMITIVES);
155        _compositeCamera->setClearMask(0);
156
157        _compositeCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
158        _compositeCamera->setViewMatrix(osg::Matrix());
159        _compositeCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
160
161        _compositeCamera->setCullCallback(new CullCallback(0, _texWidth, _texHeight, 0));
162
163        osg::StateSet* stateSet = _compositeCamera->getOrCreateStateSet();
164        stateSet->setBinName("TraversalOrderBin");
165        stateSet->setRenderBinMode(osg::StateSet::USE_RENDERBIN_DETAILS);
166
167        _root->addChild(_compositeCamera.get());
168
169        for (unsigned i = 0; i < 2; ++i) {
170#ifdef USE_TEXTURE_RECTANGLE
171            _depthTextures[i] = new osg::TextureRectangle;
172#else
173            _depthTextures[i] = new osg::Texture2D;
174#endif
175            _depthTextures[i]->setTextureSize(_texWidth, _texHeight);
176
177            _depthTextures[i]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST);
178            _depthTextures[i]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST);
179            _depthTextures[i]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER);
180            _depthTextures[i]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER);
181
182#ifdef USE_PACKED_DEPTH_STENCIL
183            _depthTextures[i]->setInternalFormat(GL_DEPTH24_STENCIL8_EXT);
184            _depthTextures[i]->setSourceFormat(GL_DEPTH_STENCIL_EXT);
185            _depthTextures[i]->setSourceType(GL_UNSIGNED_INT_24_8_EXT);
186#else
187            _depthTextures[i]->setInternalFormat(GL_DEPTH_COMPONENT);
188            _depthTextures[i]->setInternalFormat(GL_DEPTH_COMPONENT24);
189#endif
190
191            _depthTextures[i]->setShadowComparison(true);
192            _depthTextures[i]->setShadowAmbient(0); // The r value if the test fails
193            _depthTextures[i]->setShadowCompareFunc(osg::Texture::GREATER);
194            _depthTextures[i]->setShadowTextureMode(osg::Texture::INTENSITY);
195        }
196
197        // Then, the other ones
198        for (unsigned i = 0; i < _numPasses; ++i) {
199            osg::Camera* camera = new osg::Camera;
200            camera->setDataVariance(osg::Object::DYNAMIC);
201
202            camera->setInheritanceMask(osg::Camera::ALL_VARIABLES);
203            camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
204            camera->setRenderOrder(osg::Camera::PRE_RENDER, i);
205            camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
206            camera->setClearColor(osg::Vec4f(0, 0, 0, 0));
207
208            camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
209
210            osg::ref_ptr<osg::Texture> depthTexture = _depthTextures[i%2];
211            osg::ref_ptr<osg::Texture> prevDepthTexture = _depthTextures[(i+1)%2];
212
213#ifdef USE_PACKED_DEPTH_STENCIL
214            camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, depthTexture.get());
215#else
216            camera->attach(osg::Camera::DEPTH_BUFFER, depthTexture.get());
217#endif
218
219#ifdef USE_TEXTURE_RECTANGLE
220            osg::ref_ptr<osg::TextureRectangle> colorTexture = new osg::TextureRectangle;
221#else
222            osg::ref_ptr<osg::Texture2D> colorTexture = new osg::Texture2D;
223#endif
224            _colorTextures.push_back(colorTexture);
225
226            colorTexture->setTextureSize(_texWidth, _texHeight);
227            colorTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST);
228            colorTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST);
229            colorTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER);
230            colorTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER);
231            colorTexture->setInternalFormat(GL_RGBA);
232            camera->attach(osg::Camera::COLOR_BUFFER, colorTexture.get());
233
234            camera->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
235            if (0 == i) {
236                camera->addChild(_scene.get());
237            } else {
238                osg::StateSet* stateSet = camera->getOrCreateStateSet();
239
240                stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01),
241                                               osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
242
243                stateSet->setTextureAttributeAndModes(_texUnit, prevDepthTexture.get());
244
245                // Is the default ...
246                //         stateSet->setTextureAttributeAndModes(_texUnit, new osg::TexEnv(osg::TexEnv::MODULATE));
247                stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_S, osg::StateAttribute::ON);
248                stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_T, osg::StateAttribute::ON);
249                stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_R, osg::StateAttribute::ON);
250                stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON);
251
252                osg::TexGenNode* texGenNode = new osg::TexGenNode;
253                texGenNode->setReferenceFrame(osg::TexGenNode::ABSOLUTE_RF);
254                texGenNode->setTextureUnit(_texUnit);
255                texGenNode->getTexGen()->setMode(osg::TexGen::EYE_LINEAR);
256                camera->addChild(texGenNode);
257                camera->addCullCallback(new CullCallback(_texUnit, _texWidth, _texHeight, _offsetValue));
258
259                texGenNode->addChild(_scene.get());
260            }
261
262            _root->addChild(camera);
263
264            osg::Node* geode = createQuad(i, numTiles);
265            osg::StateSet* stateSet = geode->getOrCreateStateSet();
266            stateSet->setTextureAttributeAndModes(0, colorTexture.get(), osg::StateAttribute::ON);
267            stateSet->setAttribute(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON);
268            stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
269            stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
270            stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
271            _compositeCamera->insertChild(0, geode);
272        }
273    }
274
275    DepthPeeling(unsigned width, unsigned height) :
276        _numPasses(8),
277        _texUnit(1),
278        _texWidth(width),
279        _texHeight(height),
280        _showAllLayers(false),
281        _depthPeelingEnabled(true),
282        _offsetValue(8),
283        _root(new osg::Group),
284        _scene(new osg::Group)
285    {
286        createPeeling();
287    }
288
289    void setScene(osg::Node* scene)
290    {
291        _scene->removeChildren(0, _scene->getNumChildren());
292        _scene->addChild(scene);
293    }
294
295    osg::Node* getRoot()
296    {
297        return _root.get();
298    }
299
300    void resize(int width, int height)
301    {
302#ifdef USE_TEXTURE_RECTANGLE
303        _depthTextures[0]->setTextureSize(width, height);
304        _depthTextures[1]->setTextureSize(width, height);
305        for (unsigned i = 0; i < _colorTextures.size(); ++i)
306            _colorTextures[i]->setTextureSize(width, height);
307        _texWidth = width;
308        _texHeight = height;
309#else
310#ifndef USE_NON_POWER_OF_TWO_TEXTURE
311        width = nextPowerOfTwo(width);
312        height = nextPowerOfTwo(height);
313#endif
314        _depthTextures[0]->setTextureSize(width, height);
315        _depthTextures[1]->setTextureSize(width, height);
316        for (unsigned i = 0; i < _colorTextures.size(); ++i)
317            _colorTextures[i]->setTextureSize(width, height);
318        _texWidth = width;
319        _texHeight = height;
320#endif
321        createPeeling();
322    }
323
324    void setNumPasses(unsigned numPasses)
325    {
326        if (numPasses == _numPasses)
327            return;
328        if (numPasses == unsigned(-1))
329            return;
330        _numPasses = numPasses;
331        createPeeling();
332    }
333    unsigned getNumPasses() const
334    {
335        return _numPasses;
336    }
337
338    void setTexUnit(unsigned texUnit)
339    {
340        if (texUnit == _texUnit)
341            return;
342        _texUnit = texUnit;
343        createPeeling();
344    }
345
346    void setShowAllLayers(bool showAllLayers)
347    {
348        if (showAllLayers == _showAllLayers)
349            return;
350        _showAllLayers = showAllLayers;
351        createPeeling();
352    }
353    bool getShowAllLayers() const
354    {
355        return _showAllLayers;
356    }
357
358    void setDepthPeelingEnabled(bool depthPeelingEnabled)
359    {
360        if (depthPeelingEnabled == _depthPeelingEnabled)
361            return;
362        _depthPeelingEnabled = depthPeelingEnabled;
363        createPeeling();
364    }
365    bool getDepthPeelingEnabled() const
366    {
367        return _depthPeelingEnabled;
368    }
369
370    void setOffsetValue(unsigned offsetValue)
371    {
372        if (offsetValue == _offsetValue)
373            return;
374        _offsetValue = offsetValue;
375        createPeeling();
376    }
377    unsigned getOffsetValue() const
378    {
379        return _offsetValue;
380    }
381
382    unsigned _numPasses;
383    unsigned _texUnit;
384    unsigned _texWidth;
385    unsigned _texHeight;
386    bool _showAllLayers;
387    bool _depthPeelingEnabled;
388    unsigned _offsetValue;
389
390    // The root node that is handed over to the viewer
391    osg::ref_ptr<osg::Group> _root;
392
393    // The scene that is displayed
394    osg::ref_ptr<osg::Group> _scene;
395
396    // The final camera that composites the pre rendered textures to the final picture
397    osg::ref_ptr<osg::Camera> _compositeCamera;
398
399#ifdef USE_TEXTURE_RECTANGLE
400    osg::ref_ptr<osg::TextureRectangle> _depthTextures[2];
401    std::vector<osg::ref_ptr<osg::TextureRectangle> > _colorTextures;
402#else
403    osg::ref_ptr<osg::Texture2D> _depthTextures[2];
404    std::vector<osg::ref_ptr<osg::Texture2D> > _colorTextures;
405#endif
406};
407
408class EventHandler : public osgGA::GUIEventHandler {
409public:
410    EventHandler(DepthPeeling* depthPeeling) :
411        _depthPeeling(depthPeeling)
412    { }
413
414    /** Handle events, return true if handled, false otherwise. */
415    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&, osg::Object*, osg::NodeVisitor*)
416    {
417        if (ea.getEventType() == osgGA::GUIEventAdapter::RESIZE) {
418            _depthPeeling->resize(ea.getWindowWidth(), ea.getWindowHeight());
419            return true;
420        }
421
422        if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) {
423            switch (ea.getKey()) {
424            case 'd':
425                _depthPeeling->setDepthPeelingEnabled(!_depthPeeling->getDepthPeelingEnabled());
426                return true;
427            case 'm':
428                _depthPeeling->setNumPasses(_depthPeeling->getNumPasses() + 1);
429                return true;
430            case 'n':
431                _depthPeeling->setNumPasses(_depthPeeling->getNumPasses() - 1);
432                return true;
433            case 'p':
434                _depthPeeling->setOffsetValue(_depthPeeling->getOffsetValue() + 1);
435                return true;
436            case 'o':
437                _depthPeeling->setOffsetValue(_depthPeeling->getOffsetValue() - 1);
438                return true;
439            case 'l':
440                _depthPeeling->setShowAllLayers(!_depthPeeling->getShowAllLayers());
441                return true;
442            default:
443                return false;
444            };
445        }
446
447        return false;
448    }
449
450    osg::ref_ptr<DepthPeeling> _depthPeeling;
451};
452
453int main(int argc, char** argv)
454{
455    // use an ArgumentParser object to manage the program arguments.
456    osg::ArgumentParser arguments(&argc, argv);
457    arguments.getApplicationUsage()->addKeyboardMouseBinding("d", "Toggle depth peeling enabled");
458    arguments.getApplicationUsage()->addKeyboardMouseBinding("m", "Increase the number of depth peeling layers");
459    arguments.getApplicationUsage()->addKeyboardMouseBinding("n", "Decrease the number of depth peeling layers");
460    arguments.getApplicationUsage()->addKeyboardMouseBinding("l", "Toggle display of the individual or composed layer textures");
461    arguments.getApplicationUsage()->addKeyboardMouseBinding("p", "Increase the layer offset");
462    arguments.getApplicationUsage()->addKeyboardMouseBinding("o", "Decrease the layer offset");
463
464    // Have the usual viewer
465    osgViewer::Viewer viewer(arguments);
466
467    osg::DisplaySettings* displaySettings = new osg::DisplaySettings;
468    viewer.setDisplaySettings(displaySettings);
469   
470    // Add the stats handler
471    viewer.addEventHandler(new osgViewer::StatsHandler);
472   
473    // add the help handler
474    viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
475
476    // load the data
477    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
478    if (!loadedModel)
479    {
480        std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
481        return 1;
482    }
483   
484    // any option left unread are converted into errors to write out later.
485    arguments.reportRemainingOptionsAsUnrecognized();
486   
487    // report any errors if they have occurred when parsing the program arguments.
488    if (arguments.errors())
489    {
490        arguments.writeErrorMessages(std::cout);
491        return 1;
492    }
493
494    // The initial size sez to 0, 0. We get a resize event for the right size ...
495    DepthPeeling* depthPeeling = new DepthPeeling(0, 0);
496    depthPeeling->setScene(loadedModel.get());
497    viewer.setSceneData(depthPeeling->getRoot());
498
499    // Add the event handler for the depth peeling stuff
500    viewer.addEventHandler(new EventHandler(depthPeeling));
501   
502    return viewer.run();
503}
Note: See TracBrowser for help on using the browser.