root/OpenSceneGraph/trunk/examples/osgparticleshader/osgparticleshader.cpp @ 11754

Revision 11754, 8.7 kB (checked in by robert, 4 years ago)

Form Wang Rui, "An initial GLSL shader support of rendering particles. Only the POINT
type is supported at present. The attached osgparticleshader.cpp will
show how it works. It can also be placed in the examples folder. But I
just wonder how this example co-exists with another two (osgparticle
and osgparticleeffect)?

Member variables in Particle, including _alive, _current_size and
_current_alpha, are now merged into one Vec3 variable. Then we can
make use of the set...Pointer() methods to treat them as vertex
attribtues in GLSL. User interfaces are not changed.

Additional methods of ParticleSystem? are introduced, including
setDefaultAttributesUsingShaders(), setSortMode() and
setVisibilityDistance(). You can see how they work in
osgparticleshader.cpp.

Additional user-defined particle type is introduced. Set the particle
type to USER and attach a drawable to the template. Be careful because
of possible huge memory consumption. It is highly suggested to use
display lists here.

The ParticleSystemUpdater? can accepts ParticleSystem? objects as child
drawables now. I myself think it is a little simpler in structure,
than creating a new geode for each particle system. Of course, the
latter is still compatible, and can be used to transform entire
particles in the world.

New particle operators: bounce, sink, damping, orbit and explosion.
The bounce and sink opeartors both use a concept of domains, and can
simulate a very basic collision of particles and objects.

New composite placer. It contains a set of placers and emit particles
from them randomly. The added virtual method size() of each placer
will help determine the probability of generating.

New virtual method operateParticles() for the Operator class. It
actually calls operate() for each particle, but can be overrode to use
speedup techniques like SSE, or even shaders in the future.

Partly fix a floating error of 'delta time' in emitter, program and
updaters. Previously they keep the _t0 variable seperately and compute
different copies of dt by themseleves, which makes some operators,
especially the BounceOperator?, work incorrectly (because the dt in
operators and updaters are slightly different). Now a getDeltaTime()
method is maintained in ParticleSystem?, and will return the unique dt
value (passing by reference) for use. This makes thing better, but
still very few unexpected behavours at present...

All dotosg and serialzier wrappers for functionalities above are provided.

...

According to some simple tests, the new shader support is slightly
efficient than ordinary glBegin()/end(). That means, I haven't got a
big improvement at present. I think the bottlenack here seems to be
the cull traversal time. Because operators go through the particle
list again and again (for example, the fountain in the shader example
requires 4 operators working all the time).

A really ideal solution here is to implement the particle operators in
shaders, too, and copy the results back to particle attributes. The
concept of GPGPU is good for implementing this. But in my opinion, the
Camera class seems to be too heavy for realizing such functionality in
a particle system. Myabe a light-weight ComputeDrawable? class is
enough for receiving data as textures and outputting the results to
the FBO render buffer. What do you think then?

The floating error of emitters
(http://lists.openscenegraph.org/pipermail/osg-users-openscenegraph.org/2009-May/028435.html)
is not solved this time. But what I think is worth testing is that we
could directly compute the node path from the emitter to the particle
system rather than multiplying the worldToLocal and LocalToWorld?
matrices. I'll try this idea later.
"

Line 
1/* OpenSceneGraph example, osgparticleshader.
2*
3*  Permission is hereby granted, free of charge, to any person obtaining a copy
4*  of this software and associated documentation files (the "Software"), to deal
5*  in the Software without restriction, including without limitation the rights
6*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7*  copies of the Software, and to permit persons to whom the Software is
8*  furnished to do so, subject to the following conditions:
9*
10*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16*  THE SOFTWARE.
17*/
18
19#include <iostream>
20
21#include <osg/ShapeDrawable>
22#include <osg/MatrixTransform>
23#include <osg/Point>
24#include <osg/PointSprite>
25#include <osgDB/ReadFile>
26#include <osgDB/WriteFile>
27#include <osgGA/TrackballManipulator>
28#include <osgGA/StateSetManipulator>
29#include <osgViewer/ViewerEventHandlers>
30#include <osgViewer/Viewer>
31
32#include <osgParticle/ParticleSystem>
33#include <osgParticle/ParticleSystemUpdater>
34#include <osgParticle/ModularEmitter>
35#include <osgParticle/ModularProgram>
36
37#include <osgParticle/AccelOperator>
38#include <osgParticle/DampingOperator>
39#include <osgParticle/BounceOperator>
40#include <osgParticle/SinkOperator>
41
42void createFountainEffect( osgParticle::ModularEmitter* emitter, osgParticle::ModularProgram* program )
43{
44    // Emit specific number of particles every frame
45    osg::ref_ptr<osgParticle::RandomRateCounter> rrc = new osgParticle::RandomRateCounter;
46    rrc->setRateRange( 500, 2000 );
47   
48    // Accelerate particles in the given gravity direction.
49    osg::ref_ptr<osgParticle::AccelOperator> accel = new osgParticle::AccelOperator;
50    accel->setToGravity();
51   
52    // Multiply each particle's velocity by a damping constant.
53    osg::ref_ptr<osgParticle::DampingOperator> damping = new osgParticle::DampingOperator;
54    damping->setDamping( 0.9f );
55   
56    // Bounce particles off objects defined by one or more domains.
57    // Supported domains include triangle, rectangle, plane, disk and sphere.
58    // Since a bounce always happens instantaneously, it will not work correctly with unstable delta-time.
59    // At present, even the floating error of dt (which are applied to ParticleSystem and Operator seperately)
60    // causes wrong bounce results. Some one else may have better solutions for this.
61    osg::ref_ptr<osgParticle::BounceOperator> bounce = new osgParticle::BounceOperator;
62    bounce->setFriction( -0.05 );
63    bounce->setResilience( 0.35 );
64    bounce->addDiskDomain( osg::Vec3(0.0f, 0.0f, -2.0f), osg::Z_AXIS, 8.0f );
65    bounce->addPlaneDomain( osg::Plane(osg::Z_AXIS, 5.0f) );
66   
67    // Kill particles going inside/outside of specified domains.
68    osg::ref_ptr<osgParticle::SinkOperator> sink = new osgParticle::SinkOperator;
69    sink->setSinkStrategy( osgParticle::SinkOperator::SINK_OUTSIDE );
70    sink->addSphereDomain( osg::Vec3(), 20.0f );
71   
72    emitter->setCounter( rrc.get() );
73    program->addOperator( accel.get() );
74    program->addOperator( damping.get() );
75    program->addOperator( bounce.get() );
76    program->addOperator( sink.get() );
77}
78
79int main( int argc, char** argv )
80{
81    osg::ArgumentParser arguments( &argc, argv );
82   
83    std::string textureFile("Images/smoke.rgb");
84    while ( arguments.read("--texture", textureFile) ) {}
85   
86    float pointSize = 20.0f;
87    while ( arguments.read("--point", pointSize) ) {}
88   
89    double visibilityDistance = -1.0f;
90    while ( arguments.read("--visibility", visibilityDistance) ) {}
91   
92    bool customShape = false;
93    while ( arguments.read("--enable-custom") ) { customShape = true; }
94   
95    bool useShaders = true;
96    while ( arguments.read("--disable-shaders") ) { useShaders = false; }
97   
98    /***
99    Customize particle template and system attributes
100    ***/
101    osg::ref_ptr<osgParticle::ParticleSystem> ps = new osgParticle::ParticleSystem;
102   
103    osgParticle::Particle& ptemp = ps->getDefaultParticleTemplate();
104    ps->getDefaultParticleTemplate().setLifeTime( 5.0f );
105   
106    if ( customShape )
107    {
108        // osgParticle now supports making use of customized drawables. The draw() method will be executed
109        // and display lists will be called for each particle. It is always a huge consumption of memory, and
110        // hardly to use shaders to render them, so please be careful using this feature.
111        ps->getDefaultParticleTemplate().setShape( osgParticle::Particle::USER );
112        ps->getDefaultParticleTemplate().setDrawable( new osg::ShapeDrawable(new osg::Box(osg::Vec3(), 1.0f)) );
113        useShaders = false;
114    }
115    else
116    {
117        // The shader only supports rendering points at present.
118        ps->getDefaultParticleTemplate().setShape( osgParticle::Particle::POINT );
119    }
120   
121    // Set the visibility distance of particles, due to their Z-value in the eye coordinates.
122    // Particles that are out of the distance (or behind the eye) will not be rendered.
123    ps->setVisibilityDistance( visibilityDistance );
124   
125    if ( useShaders )
126    {
127        // Set using local GLSL shaders to render particles.
128        // At present, this is slightly efficient than ordinary methods. The bottlenack here seems to be the cull
129        // traversal time. Operators go through the particle list again and again...
130        ps->setDefaultAttributesUsingShaders( textureFile, true, 0 );
131    }
132    else
133    {
134        // The default methods uses glBegin()/glEnd() pairs. Fortunately the GLBeginEndAdapter does improve the
135        // process, which mimics the immediate mode with glDrawArrays().
136        ps->setDefaultAttributes( textureFile, true, false, 0 );
137       
138        // Without the help of shaders, we have to sort particles to make the visibility distance work. Sorting is
139        // also useful in rendering transparent particles in back-to-front order.
140        if ( visibilityDistance>0.0 )
141            ps->setSortMode( osgParticle::ParticleSystem::SORT_BACK_TO_FRONT );
142    }
143   
144    // At last, to make the point sprite work, we have to set the points size and the sprite attribute.
145    osg::StateSet* stateset = ps->getOrCreateStateSet();
146    stateset->setAttribute( new osg::Point(pointSize) );
147    stateset->setTextureAttributeAndModes( 0, new osg::PointSprite, osg::StateAttribute::ON );
148   
149    /***
150    Construct other particle system elements, including the emitter and program
151    ***/
152    osg::ref_ptr<osgParticle::ModularEmitter> emitter = new osgParticle::ModularEmitter;
153    emitter->setParticleSystem( ps.get() );
154   
155    osg::ref_ptr<osgParticle::ModularProgram> program = new osgParticle::ModularProgram;
156    program->setParticleSystem( ps.get() );
157   
158    createFountainEffect( emitter.get(), program.get() );
159   
160    /***
161    Add the entire particle system to the scene graph
162    ***/
163    osg::ref_ptr<osg::MatrixTransform> parent = new osg::MatrixTransform;
164    parent->addChild( emitter.get() );
165    parent->addChild( program.get() );
166   
167    // The updater can receive particle systems as child drawables now. The addParticleSystem() method
168    // is still usable, with which we should define another geode to contain a particle system.
169    osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater = new osgParticle::ParticleSystemUpdater;
170    updater->addDrawable( ps.get() );
171   
172    osg::ref_ptr<osg::Group> root = new osg::Group;
173    root->addChild( parent.get() );
174    root->addChild( updater.get() );
175   
176    /***
177    Start the viewer
178    ***/
179    osgViewer::Viewer viewer;
180    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
181    viewer.addEventHandler( new osgViewer::StatsHandler );
182    viewer.addEventHandler( new osgViewer::WindowSizeHandler );
183    viewer.setSceneData( root.get() );
184    viewer.setCameraManipulator( new osgGA::TrackballManipulator );
185   
186    // A floating error of delta-time should be explained here:
187    // The particles emitter, program and updater all use a 'dt' to compute the time value in every frame.
188    // Because the 'dt' is a double value, it is not suitable to keep three copies of it seperately, which
189    // is the previous implementation. The small error makes some opeartors unable to work correctly, e.g.
190    // the BounceOperator.
191    // Now we make use of the getDeltaTime() of ParticleSystem to maintain and dispatch the delta time. But..
192    // it is not the best solution so far, since there are still very few particles acting unexpectedly.
193    return viewer.run();
194}
Note: See TracBrowser for help on using the browser.