diff --git a/CMakeLists.txt b/CMakeLists.txt
index ce8262e6c5a5549476cad778045aafdc1e984590..266b12686ba6692a87025029d893fb051a95acc1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,7 @@
 cmake_minimum_required( VERSION 3.1 FATAL_ERROR )
 
 # visimpl project and version
-project( visimpl VERSION 1.8.2 )
+project( visimpl VERSION 1.8.3 )
 set( visimpl_VERSION_ABI 6 )
 
 SET( VISIMPL_LICENSE "GPL")
diff --git a/visimpl/DomainManager.cpp b/visimpl/DomainManager.cpp
index 27ae227d9ca998413d6f30d03aa52b7ec368e185..f3be64bddd82bd25f2a6c28fdd732d8cd7b8bdab 100644
--- a/visimpl/DomainManager.cpp
+++ b/visimpl/DomainManager.cpp
@@ -24,6 +24,8 @@ namespace visimpl
     , _currentRenderer( nullptr )
     , _defaultRenderer( nullptr )
     , _solidRenderer( nullptr )
+    , _solidMode( false )
+    , _accumulativeMode( false )
     , _decay( 0.0f )
   {
   }
@@ -62,15 +64,48 @@ namespace visimpl
       visimpl::PARTICLE_SOLID_FRAGMENT_SHADER );
     _solidProgram.compileAndLink( );
 
-    _defaultRenderer = std::make_shared< plab::CoverageRenderer >(
+    _defaultAccProgram.loadFromText(
+      visimpl::PARTICLE_VERTEX_SHADER ,
+      visimpl::PARTICLE_ACC_DEFAULT_FRAGMENT_SHADER );
+    _defaultAccProgram.compileAndLink( );
+
+    _solidAccProgram.loadFromText(
+      visimpl::PARTICLE_VERTEX_SHADER ,
+      visimpl::PARTICLE_ACC_SOLID_FRAGMENT_SHADER );
+    _solidAccProgram.compileAndLink( );
+
+    _defaultRenderer = std::make_shared< plab::SimpleRenderer >(
       _defaultProgram.program( ));
-    _solidRenderer = std::make_shared< plab::CoverageRenderer >(
+    _solidRenderer = std::make_shared< plab::SimpleRenderer >(
       _solidProgram.program( ));
 
-    _currentRenderer = _defaultRenderer;
+    _defaultAccRenderer = std::make_shared< plab::SimpleRenderer >(
+      _defaultAccProgram.program( ));
+    _solidAccRenderer = std::make_shared< plab::SimpleRenderer >(
+      _solidAccProgram.program( ));
 
     _selectionCluster->setModel( _selectionModel );
-    _selectionCluster->setRenderer( _currentRenderer );
+
+    refreshRenderer( );
+  }
+
+  void DomainManager::refreshRenderer( )
+  {
+    if ( _accumulativeMode )
+    {
+      _currentRenderer = _solidMode ? _solidAccRenderer : _defaultAccRenderer;
+    }
+    else
+    {
+      _currentRenderer = _solidMode ? _solidRenderer : _defaultRenderer;
+    }
+
+    if ( _selectionCluster != nullptr )
+      _selectionCluster->setRenderer( _currentRenderer );
+    for ( const auto& item: _groupClusters )
+      item.second->setRenderer( _currentRenderer );
+    for ( const auto& item: _attributeClusters )
+      item.second->setRenderer( _currentRenderer );
   }
 
 #ifdef SIMIL_USE_BRION
@@ -291,8 +326,7 @@ namespace visimpl
       _currentRenderer ,
       _selectionModel->isClippingEnabled( ));
 
-    group->getModel( )->setAccumulativeMode(
-      _selectionModel->isAccumulativeMode( ));
+    group->getModel( )->setAccumulativeMode( _accumulativeMode );
 
     std::vector< uint32_t > ids;
     std::vector< NeuronParticle > particles;
@@ -326,8 +360,7 @@ namespace visimpl
       _currentRenderer ,
       _selectionModel->isClippingEnabled( ));
 
-    group->getModel( )->setAccumulativeMode(
-      _selectionModel->isAccumulativeMode( ));
+    group->getModel( )->setAccumulativeMode( _accumulativeMode );
 
     std::vector< NeuronParticle > particles;
     for ( const auto& gid: _selectionGids )
@@ -345,7 +378,7 @@ namespace visimpl
 
   void DomainManager::removeGroup( const std::string& name )
   {
-    if(1 != _groupClusters.erase( name ))
+    if ( 1 != _groupClusters.erase( name ))
     {
       std::cerr << "DomainManager: Error removing group '" << name
                 << "' - " << __FILE__ << ":" << __LINE__ << std::endl;
@@ -373,8 +406,7 @@ namespace visimpl
         _selectionModel->getRightPlane( ) ,
         _currentRenderer ,
         _selectionModel->isClippingEnabled( ));
-      group->getModel( )->setAccumulativeMode(
-        _selectionModel->isAccumulativeMode( ));
+      group->getModel( )->setAccumulativeMode( _accumulativeMode );
 
       const auto currentIndex = i % colors.size( );
       const auto color = colors[ currentIndex ].toRgb( );
@@ -402,14 +434,21 @@ namespace visimpl
 
   }
 
+  bool DomainManager::isAccumulativeModeEnabled( )
+  {
+    return _accumulativeMode;
+  }
+
   void DomainManager::enableAccumulativeMode( bool enabled )
   {
+    _accumulativeMode = enabled;
     if ( _selectionModel != nullptr )
     {
       _selectionModel->setAccumulativeMode( enabled );
     }
     for ( const auto& item: _groupClusters )
       item.second->getModel( )->setAccumulativeMode( enabled );
+    refreshRenderer( );
   }
 
   void DomainManager::enableClipping( bool enabled )
@@ -456,22 +495,14 @@ namespace visimpl
 
   void DomainManager::applyDefaultShader( )
   {
-    _currentRenderer = _defaultRenderer;
-    _selectionCluster->setRenderer( _currentRenderer );
-    for ( const auto& item: _groupClusters )
-      item.second->setRenderer( _currentRenderer );
-    for ( const auto& item: _attributeClusters )
-      item.second->setRenderer( _currentRenderer );
+    _solidMode = false;
+    refreshRenderer( );
   }
 
   void DomainManager::applySolidShader( )
   {
-    _currentRenderer = _solidRenderer;
-    _selectionCluster->setRenderer( _currentRenderer );
-    for ( const auto& item: _groupClusters )
-      item.second->setRenderer( _currentRenderer );
-    for ( const auto& item: _attributeClusters )
-      item.second->setRenderer( _currentRenderer );
+    _solidMode = true;
+    refreshRenderer( );
   }
 
   void DomainManager::processInput(
@@ -551,7 +582,7 @@ namespace visimpl
         {
           particle->timestamp = -std::numeric_limits< float >::infinity( );
         }
-        auto gid = item.second->getGids().at( i );
+        auto gid = item.second->getGids( ).at( i );
         auto value = input.find( gid );
         if ( value != input.cend( ))
         {
@@ -580,7 +611,7 @@ namespace visimpl
         {
           particle->timestamp = -std::numeric_limits< float >::infinity( );
         }
-        auto gid = item.second->getGids().at( i );
+        auto gid = item.second->getGids( ).at( i );
         auto value = input.find( gid );
         if ( value != input.cend( ))
         {
diff --git a/visimpl/DomainManager.h b/visimpl/DomainManager.h
index 2a8dc65d60d3809949a5c49b0facb282eba2ee01..bd051039b08254dabddb55dfd4fc478a508c0ac4 100644
--- a/visimpl/DomainManager.h
+++ b/visimpl/DomainManager.h
@@ -47,10 +47,16 @@ namespace visimpl
     // Renders
     reto::ShaderProgram _defaultProgram;
     reto::ShaderProgram _solidProgram;
+    reto::ShaderProgram _defaultAccProgram;
+    reto::ShaderProgram _solidAccProgram;
 
-    std::shared_ptr< plab::CoverageRenderer > _currentRenderer;
-    std::shared_ptr< plab::CoverageRenderer > _defaultRenderer;
-    std::shared_ptr< plab::CoverageRenderer > _solidRenderer;
+    std::shared_ptr< plab::SimpleRenderer > _currentRenderer;
+    std::shared_ptr< plab::SimpleRenderer > _defaultRenderer;
+    std::shared_ptr< plab::SimpleRenderer > _solidRenderer;
+    std::shared_ptr< plab::SimpleRenderer > _defaultAccRenderer;
+    std::shared_ptr< plab::SimpleRenderer > _solidAccRenderer;
+
+    bool _solidMode , _accumulativeMode;
 
     // Others
     tBoundingBox _boundingBox;
@@ -65,6 +71,8 @@ namespace visimpl
       const std::shared_ptr< reto::ClippingPlane >& rightPlane ,
       const std::shared_ptr< plab::ICamera >& camera );
 
+    void refreshRenderer( );
+
 #ifdef SIMIL_USE_BRION
 
     void initAttributeData( const TGIDSet& gids ,
@@ -97,7 +105,7 @@ namespace visimpl
 
     void setTime( float time );
 
-    void addTime( float time, float endTime );
+    void addTime( float time , float endTime );
 
     const tBoundingBox& getBoundingBox( ) const;
 
@@ -125,6 +133,8 @@ namespace visimpl
 
     void applySolidShader( );
 
+    bool isAccumulativeModeEnabled( );
+
     void enableAccumulativeMode( bool enabled );
 
     void enableClipping( bool enabled );
diff --git a/visimpl/OpenGLWidget.cpp b/visimpl/OpenGLWidget.cpp
index 9367a6b173971b960135a82d7714fa60970a8eea..078d3f4dc3b0a045bd18878e847a6f6b9fc75776 100644
--- a/visimpl/OpenGLWidget.cpp
+++ b/visimpl/OpenGLWidget.cpp
@@ -61,8 +61,6 @@
 constexpr float ZOOM_FACTOR = 1.3f;
 constexpr float TRANSLATION_FACTOR = 0.001f;
 constexpr float ROTATION_FACTOR = 0.01f;
-constexpr int FRAMEBUFFER_SCALE_FACTOR = 2;
-constexpr int SAMPLES = 4;
 constexpr float DEFAULT_DELTA_TIME = 0.5f;
 const QString INITIAL_CAMERA_POSITION = "0,0,0;1;1,0,0,0,1,0,0,0,1";
 
@@ -81,44 +79,18 @@ namespace visimpl
 
   constexpr float invRGBInt = 1.0f / 255;
 
-  const static std::string vertexShaderCode = R"(#version 330 core
-layout (location = 0) in vec2 aPos;
-layout (location = 1) in vec2 aTexCoords;
-
-out vec2 TexCoords;
-
-void main()
-{
-    gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);
-    TexCoords = aTexCoords;
-}
-)";
-
-  const static std::string screenFragment = R"(#version 330 core
-out vec4 FragColor;
-
-in vec2 TexCoords;
-
-uniform sampler2D screenTexture;
-
-void main()
-{
-    FragColor = vec4(texture(screenTexture, TexCoords).rgb, 1);
-}
-)";
-
-
   OpenGLWidget::OpenGLWidget( QWidget* parent_ ,
                               Qt::WindowFlags windowsFlags_ ,
                               const std::string&
 #ifdef VISIMPL_USE_ZEROEQ
-                              zeqUri
+    zeqUri
 #endif
                             )
     : QOpenGLWidget( parent_ , windowsFlags_ )
 #ifdef VISIMPL_USE_ZEROEQ
     , _zeqUri( zeqUri )
 #endif
+    , _gl( )
     , _fpsLabel( nullptr )
     , _labelCurrentTime( nullptr )
     , _showFps( false )
@@ -189,12 +161,10 @@ void main()
     , _deltaEvents( 0.125f )
     , _domainManager( )
     , _screenPlaneShader( nullptr )
-    , _msaaFrameBuffer( 0 )
-    , _msaaTextureColor( 0 )
-    , _msaaRBODepth( 0 )
-    , _midFrameBuffer( 0 )
-    , _midTextureColor( 0 )
-    , _midRBODepth( 0 )
+    , _quadVAO( 0 )
+    , _weightFrameBuffer( 0 )
+    , _accumulationTexture( 0 )
+    , _revealTexture( 0 )
   {
     _lastCameraPosition = glm::vec3( 0 , 0 , 0 );
 
@@ -277,7 +247,7 @@ void main()
       }
     }
 #else
-      _camera = std::make_shared< Camera >(  );
+    _camera = std::make_shared< Camera >( );
 #endif
     Q_ASSERT( _camera );
 
@@ -292,7 +262,7 @@ void main()
 
   void OpenGLWidget::initializeGL( void )
   {
-    initializeOpenGLFunctions( );
+    _gl.initializeOpenGLFunctions( );
 
     auto* logger = new QOpenGLDebugLogger( this );
     logger->initialize( );
@@ -316,8 +286,6 @@ void main()
     glPolygonMode( GL_FRONT_AND_BACK , GL_FILL );
     glEnable( GL_CULL_FACE );
 
-    glLineWidth( 1.5 );
-
     _then = std::chrono::system_clock::now( );
     _lastFrame = std::chrono::system_clock::now( );
 
@@ -331,13 +299,14 @@ void main()
     const GLubyte* shadingVer = glGetString( GL_SHADING_LANGUAGE_VERSION );
 
     _screenPlaneShader = new reto::ShaderProgram( );
-    _screenPlaneShader->loadVertexShaderFromText( vertexShaderCode );
-    _screenPlaneShader->loadFragmentShaderFromText( screenFragment );
+    _screenPlaneShader->loadVertexShaderFromText( SHADER_SCREEN_VERTEX );
+    _screenPlaneShader->loadFragmentShaderFromText( SHADER_SCREEN_FRAGMENT );
     _screenPlaneShader->create( );
     _screenPlaneShader->link( );
     _screenPlaneShader->autocatching( true );
     _screenPlaneShader->use( );
-    _screenPlaneShader->sendUniformi( "screenTexture" , 0 );
+    _screenPlaneShader->sendUniformi( "accumulation" , 0 );
+    _screenPlaneShader->sendUniformi( "reveal" , 1 );
 
 
     std::cout << "OpenGL Hardware: " << vendor << " (" << renderer << ")"
@@ -348,84 +317,66 @@ void main()
 
   void OpenGLWidget::_initRenderToTexture( void )
   {
-    _screenPlaneShader = new reto::ShaderProgram( );
-    _screenPlaneShader->loadVertexShaderFromText( vertexShaderCode );
-    _screenPlaneShader->loadFragmentShaderFromText( screenFragment );
-    _screenPlaneShader->create( );
-    _screenPlaneShader->link( );
-    _screenPlaneShader->autocatching( true );
-    _screenPlaneShader->use( );
-    _screenPlaneShader->sendUniformi( "screenTexture" , 0 );
-
-    // Generate the MSAA framebuffer
 
-    glGenFramebuffers( 1 , &_msaaFrameBuffer );
-    glBindFramebuffer( GL_FRAMEBUFFER , _msaaFrameBuffer );
+    // Generate the OIR framebuffer
 
-    glGenTextures( 1 , &_msaaTextureColor );
-    glBindTexture( GL_TEXTURE_2D_MULTISAMPLE , _msaaTextureColor );
-
-    glTexImage2DMultisample(
-      GL_TEXTURE_2D_MULTISAMPLE , SAMPLES , GL_RGB ,
-      width( ) * FRAMEBUFFER_SCALE_FACTOR ,
-      height( ) * FRAMEBUFFER_SCALE_FACTOR ,
-      GL_TRUE
-    );
+    _gl.glGenFramebuffers( 1 , &_weightFrameBuffer );
+    _gl.glBindFramebuffer( GL_FRAMEBUFFER , _weightFrameBuffer );
 
-    glBindTexture( GL_TEXTURE_2D_MULTISAMPLE , 0 );
-
-    glFramebufferTexture2D( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 ,
-                            GL_TEXTURE_2D_MULTISAMPLE , _msaaTextureColor , 0 );
+    glGenTextures( 1 , &_accumulationTexture );
+    glBindTexture( GL_TEXTURE_2D , _accumulationTexture );
+    glTexImage2D( GL_TEXTURE_2D , 0 , GL_RGBA32F , width( ) , height( ) , 0 ,
+                  GL_RGBA , GL_FLOAT , nullptr );
+    glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
+    glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
 
+    glGenTextures( 1 , &_revealTexture );
+    glBindTexture( GL_TEXTURE_2D , _revealTexture );
+    glTexImage2D( GL_TEXTURE_2D , 0 , GL_R8 , width( ) , height( ) , 0 ,
+                  GL_RED , GL_FLOAT , nullptr );
+    glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
+    glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
 
-    glGenRenderbuffers( 1 , &_msaaRBODepth );
-    glBindRenderbuffer( GL_RENDERBUFFER , _msaaRBODepth );
-    glRenderbufferStorageMultisample(
-      GL_RENDERBUFFER , SAMPLES , GL_DEPTH_COMPONENT32 ,
-      width( ) * FRAMEBUFFER_SCALE_FACTOR ,
-      height( ) * FRAMEBUFFER_SCALE_FACTOR );
-    glBindRenderbuffer( GL_RENDERBUFFER , 0 );
+    _gl.glFramebufferTexture2D( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 ,
+                                GL_TEXTURE_2D , _accumulationTexture , 0 );
+    _gl.glFramebufferTexture2D( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT1 ,
+                                GL_TEXTURE_2D , _revealTexture , 0 );
 
-    glFramebufferRenderbuffer( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT ,
-                               GL_RENDERBUFFER , _msaaRBODepth );
+    const GLenum transparentDrawBuffers[] = { GL_COLOR_ATTACHMENT0 ,
+                                              GL_COLOR_ATTACHMENT1 };
+    _gl.glDrawBuffers( 2 , transparentDrawBuffers );
 
-    if ( glCheckFramebufferStatus( GL_FRAMEBUFFER ) != GL_FRAMEBUFFER_COMPLETE )
+    if ( _gl.glCheckFramebufferStatus( GL_FRAMEBUFFER ) !=
+         GL_FRAMEBUFFER_COMPLETE )
       std::cerr << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!"
                 << std::endl;
 
-    // Generate the mid framebuffer.
+    _gl.glBindFramebuffer( GL_FRAMEBUFFER , defaultFramebufferObject( ));
 
-    glGenFramebuffers( 1 , &_midFrameBuffer );
-    glBindFramebuffer( GL_FRAMEBUFFER , _midFrameBuffer );
+    float quadVertices[] = {
+      // positions
+      -1.0f , -1.0f , 0.0f ,
+      1.0f , -1.0f , 0.0f ,
+      1.0f , 1.0f , 0.0f ,
 
-    glGenTextures( 1 , &_midTextureColor );
-    glBindTexture( GL_TEXTURE_2D , _midTextureColor );
-    glTexImage2D( GL_TEXTURE_2D , 0 , GL_RGB ,
-                  width( ) * FRAMEBUFFER_SCALE_FACTOR ,
-                  height( ) * FRAMEBUFFER_SCALE_FACTOR ,
-                  0 ,
-                  GL_RGB , GL_UNSIGNED_BYTE , nullptr );
-    glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
-    glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
-    glBindTexture( GL_TEXTURE_2D , 0 );
-    glFramebufferTexture2D( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 ,
-                            GL_TEXTURE_2D , _midTextureColor , 0 );
-
-    glGenRenderbuffers( 1 , &_midRBODepth );
-    glBindRenderbuffer( GL_RENDERBUFFER , _midRBODepth );
-    glRenderbufferStorage( GL_RENDERBUFFER , GL_DEPTH_COMPONENT32 ,
-                           width( ) * FRAMEBUFFER_SCALE_FACTOR ,
-                           height( ) * FRAMEBUFFER_SCALE_FACTOR );
-    glBindRenderbuffer( GL_RENDERBUFFER , 0 );
-
-    glFramebufferRenderbuffer( GL_FRAMEBUFFER , GL_DEPTH_ATTACHMENT ,
-                               GL_RENDERBUFFER , _midRBODepth );
-
-    if ( glCheckFramebufferStatus( GL_FRAMEBUFFER ) != GL_FRAMEBUFFER_COMPLETE )
-      std::cerr << "ERROR::FRAMEBUFFER:: Mid Framebuffer is not complete!"
-                << std::endl;
+      1.0f , 1.0f , 0.0f ,
+      -1.0f , 1.0f , 0.0f ,
+      -1.0f , -1.0f , 0.0f
+    };
 
-    glBindFramebuffer( GL_FRAMEBUFFER , defaultFramebufferObject( ));
+    // quad VAO
+    unsigned int quadVBO;
+    _gl.glGenVertexArrays( 1 , &_quadVAO );
+    _gl.glGenBuffers( 1 , &quadVBO );
+    _gl.glBindVertexArray( _quadVAO );
+    _gl.glBindBuffer( GL_ARRAY_BUFFER , quadVBO );
+    _gl.glBufferData( GL_ARRAY_BUFFER , sizeof( quadVertices ) , quadVertices ,
+                      GL_STATIC_DRAW );
+    _gl.glEnableVertexAttribArray( 0 );
+    _gl.glVertexAttribPointer( 0 , 3 , GL_FLOAT , GL_FALSE ,
+                               3 * sizeof( float ) ,
+                               ( void* ) 0 );
+    _gl.glBindVertexArray( 0 );
   }
 
   void OpenGLWidget::_configureSimulationFrame( void )
@@ -738,39 +689,60 @@ void main()
 
       } // if player && player->isPlaying
 
-
-      glBindFramebuffer( GL_FRAMEBUFFER , _msaaFrameBuffer );
-      glViewport( 0 , 0 , width( ) * FRAMEBUFFER_SCALE_FACTOR ,
-                  height( ) * FRAMEBUFFER_SCALE_FACTOR );
-      glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
-      glEnable( GL_DEPTH_TEST );
-
-      _paintPlanes( );
-      _paintParticles( );
-
       glViewport( 0 , 0 , width( ) , height( ));
 
-      // Perform MSAA
-      glBindFramebuffer( GL_READ_FRAMEBUFFER , _msaaFrameBuffer );
-      glBindFramebuffer( GL_DRAW_FRAMEBUFFER , _midFrameBuffer );
-      int w = width( ) * FRAMEBUFFER_SCALE_FACTOR;
-      int h = height( ) * FRAMEBUFFER_SCALE_FACTOR;
-      glBlitFramebuffer(
-        0 , 0 ,
-        w , h ,
-        0 , 0 ,
-        w , h ,
-        GL_COLOR_BUFFER_BIT , GL_NEAREST );
-
-      // Perform super-sampling
-      glBindFramebuffer( GL_READ_FRAMEBUFFER , _midFrameBuffer );
-      glBindFramebuffer( GL_DRAW_FRAMEBUFFER , defaultFramebufferObject( ));
-      glBlitFramebuffer(
-        0 , 0 ,
-        w , h ,
-        0 , 0 ,
-        width( ) , height( ) ,
-        GL_COLOR_BUFFER_BIT , GL_LINEAR );
+      if ( _domainManager.isAccumulativeModeEnabled( ))
+      {
+        _gl.glBindFramebuffer( GL_FRAMEBUFFER , defaultFramebufferObject( ));
+        _gl.glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+        _gl.glDisable( GL_DEPTH_TEST );
+        _gl.glDepthMask( GL_FALSE );
+        _gl.glEnable( GL_BLEND );
+        _gl.glBlendFunc( GL_SRC_ALPHA , GL_ONE_MINUS_CONSTANT_ALPHA );
+        _gl.glBlendEquation( GL_FUNC_ADD );
+        _paintPlanes( );
+        _paintParticles( );
+      }
+      else
+      {
+        _gl.glBindFramebuffer( GL_FRAMEBUFFER , _weightFrameBuffer );
+        _gl.glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+        _gl.glEnable( GL_DEPTH_TEST );
+
+
+        _gl.glDepthMask( GL_FALSE );
+        _gl.glEnable( GL_BLEND );
+        _gl.glBlendFunci( 0 , GL_ONE , GL_ONE );
+        _gl.glBlendFunci( 1 , GL_ZERO , GL_ONE_MINUS_SRC_COLOR );
+        _gl.glBlendEquation( GL_FUNC_ADD );
+
+        glm::vec4 zeroFillerVec( 0.0f );
+        glm::vec4 oneFillerVec( 1.0f );
+        _gl.glClearBufferfv( GL_COLOR , 0 , &zeroFillerVec[ 0 ] );
+        _gl.glClearBufferfv( GL_COLOR , 1 , &oneFillerVec[ 0 ] );
+
+        _paintPlanes( );
+        _paintParticles( );
+
+        // Perform blend
+
+        _gl.glDepthFunc( GL_ALWAYS );
+        _gl.glEnable( GL_BLEND );
+        _gl.glBlendFunc( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
+
+        _gl.glBindFramebuffer( GL_FRAMEBUFFER , defaultFramebufferObject( ));
+        _gl.glClear(
+          GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
+
+        _screenPlaneShader->use( );
+        _gl.glActiveTexture( GL_TEXTURE0 );
+        _gl.glBindTexture( GL_TEXTURE_2D , _accumulationTexture );
+        _gl.glActiveTexture( GL_TEXTURE1 );
+        _gl.glBindTexture( GL_TEXTURE_2D , _revealTexture );
+        _gl.glBindVertexArray( _quadVAO );
+        _gl.glDrawArrays( GL_TRIANGLES , 0 , 6 );
+      }
+
     }
 
     if ( _player && _elapsedTimeSliderAcc > _sliderUpdatePeriodMicroseconds )
@@ -1055,7 +1027,7 @@ void main()
   {
     if ( _player && ( _player->isPlaying( ) || _firstFrame ))
     {
-      _domainManager.addTime( renderDelta, _player->endTime() );
+      _domainManager.addTime( renderDelta , _player->endTime( ));
       _firstFrame = false;
     }
   }
@@ -1111,35 +1083,21 @@ void main()
     //  _pickRenderer->setWindowSize( w , h );
     //}
 
-    if ( _msaaTextureColor != 0 )
+    if ( _accumulationTexture != 0 )
     {
-      int rw = width( ) * FRAMEBUFFER_SCALE_FACTOR;
-      int rh = height( ) * FRAMEBUFFER_SCALE_FACTOR;
       // Resize MSAA buffers
-      glBindTexture( GL_TEXTURE_2D_MULTISAMPLE , _msaaTextureColor );
-      glTexImage2DMultisample(
-        GL_TEXTURE_2D_MULTISAMPLE , SAMPLES , GL_RGB ,
-        rw , rh , GL_TRUE );
-
-      glBindRenderbuffer( GL_RENDERBUFFER , _msaaRBODepth );
-      glRenderbufferStorageMultisample(
-        GL_RENDERBUFFER , SAMPLES , GL_DEPTH_COMPONENT32 ,
-        rw , rh );
-
-      // And mid-buffers too!
-
-      glBindTexture( GL_TEXTURE_2D , _midTextureColor );
-      glTexImage2D( GL_TEXTURE_2D , 0 , GL_RGB ,
-                    rw , rh , 0 ,
-                    GL_RGB , GL_UNSIGNED_BYTE , nullptr );
-
-      glBindRenderbuffer( GL_RENDERBUFFER , _midRBODepth );
-      glRenderbufferStorage( GL_RENDERBUFFER , GL_DEPTH_COMPONENT32 ,
-                             rw , rh );
-
-      glBindTexture( GL_TEXTURE_2D_MULTISAMPLE , 0 );
-      glBindTexture( GL_TEXTURE_2D , 0 );
-      glBindRenderbuffer( GL_RENDERBUFFER , 0 );
+      _gl.glBindTexture( GL_TEXTURE_2D , _accumulationTexture );
+      _gl.glTexImage2D( GL_TEXTURE_2D , 0 , GL_RGBA32F , width( ) , height( ) ,
+                        0 ,
+                        GL_RGBA , GL_FLOAT , nullptr );
+
+      _gl.glBindTexture( GL_TEXTURE_2D , _revealTexture );
+      _gl.glTexImage2D( GL_TEXTURE_2D , 0 , GL_R8 , width( ) , height( ) , 0 ,
+                        GL_RED , GL_FLOAT , nullptr );
+
+      _gl.glBindTexture( GL_TEXTURE_2D_MULTISAMPLE , 0 );
+      _gl.glBindTexture( GL_TEXTURE_2D , 0 );
+      //_gl.glBindRenderbuffer( GL_RENDERBUFFER , defaultFramebufferObject( ));
     }
   }
 
diff --git a/visimpl/OpenGLWidget.h b/visimpl/OpenGLWidget.h
index 421376b872441e0021f464e87bd988258abdb1d5..252d7136cdeeb8c542f2f39c966b0cc287257c17 100644
--- a/visimpl/OpenGLWidget.h
+++ b/visimpl/OpenGLWidget.h
@@ -50,6 +50,7 @@
 #include <plab/plab.h>
 
 #include <sstream>
+#include <QOpenGLFunctions_4_0_Core>
 
 #include "types.h"
 #include "render/Plane.h"
@@ -157,8 +158,7 @@ namespace visimpl
     AB_REPEAT
   } TPlaybackMode;
 
-  class OpenGLWidget
-    : public QOpenGLWidget , public QOpenGLFunctions_3_3_Core
+  class OpenGLWidget : public QOpenGLWidget
   {
   Q_OBJECT;
 
@@ -443,6 +443,8 @@ namespace visimpl
 
 #endif
 
+    QOpenGLFunctions_4_0_Core _gl;
+
     QLabel* _fpsLabel;
     QLabel* _labelCurrentTime;
     bool _showFps;
@@ -481,8 +483,8 @@ namespace visimpl
     reto::ShaderProgram* _shaderClippingPlanes;
     simil::SpikesPlayer* _player;
 
-    std::shared_ptr<reto::ClippingPlane> _clippingPlaneLeft;
-    std::shared_ptr<reto::ClippingPlane> _clippingPlaneRight;
+    std::shared_ptr< reto::ClippingPlane > _clippingPlaneLeft;
+    std::shared_ptr< reto::ClippingPlane > _clippingPlaneRight;
     evec3 _planesCenter;
     evec3 _planeNormalLeft;
     evec3 _planeNormalRight;
@@ -570,13 +572,10 @@ namespace visimpl
     // Render to texture
     reto::ShaderProgram* _screenPlaneShader;
 
-    unsigned int _msaaFrameBuffer;
-    unsigned int _msaaTextureColor;
-    unsigned int _msaaRBODepth;
-
-    unsigned int _midFrameBuffer;
-    unsigned int _midTextureColor;
-    unsigned int _midRBODepth;
+    unsigned int _quadVAO;
+    unsigned int _weightFrameBuffer;
+    unsigned int _accumulationTexture;
+    unsigned int _revealTexture;
   };
 } // namespace visimpl
 
diff --git a/visimpl/particlelab/ParticleLabShaders.h b/visimpl/particlelab/ParticleLabShaders.h
index 896cc5756279e73a34d07a88e69b0fdc8eba4abe..0eb42eea18ddd9c92fae6b95d04fac10b4007533 100644
--- a/visimpl/particlelab/ParticleLabShaders.h
+++ b/visimpl/particlelab/ParticleLabShaders.h
@@ -28,7 +28,7 @@
 namespace visimpl
 {
 
-const static std::string PARTICLE_VERTEX_SHADER = R"(#version 400
+  const static std::string PARTICLE_VERTEX_SHADER = R"(#version 330
 //#extension GL_ARB_separate_shader_objects: enable
 
 uniform mat4 viewProjectionMatrix;
@@ -117,26 +117,56 @@ void main()
 
 )";
 
-const static std::string PARTICLE_DEFAULT_FRAGMENT_SHADER = R"(
-#version 400
-in vec4 color; 
+  const static std::string PARTICLE_DEFAULT_FRAGMENT_SHADER = R"(#version 330
+in vec4 color;
 in vec2 uvCoord;
-out vec4 outputColor;
+
+layout (location = 0) out vec4 accumulation;
+layout (location = 1) out float reveal;
+
 void main()
 {
-  vec2 p = -1.0 + 2.0 * uvCoord;
-  float l = sqrt(dot(p,p));
-  l = 1.0 - clamp(l, 0.0, 1.0);
-  l *= color.a;
-  outputColor = vec4(color.rgb, l);
+    vec2 p = -1.0 + 2.0 * uvCoord;
+    float l = sqrt(dot(p, p));
+    l = 1.0 - clamp(l, 0.0, 1.0);
+    l *= color.a;
+
+    vec4 c = vec4(color.rgb, l);
+
+    float weight = clamp(pow(min(1.0, c.a * 10.0) + 0.01, 3.0) * 1e8
+    * pow(1.0 - gl_FragCoord.z * 0.9, 3.0), 1e-2, 3e3);
+
+    accumulation = vec4 (c.rgb * c.a, c.a) * weight;
+    reveal = c.a;
 }
 
 )";
 
-const static std::string PARTICLE_SOLID_FRAGMENT_SHADER = R"(#version 400
+  const static std::string PARTICLE_ACC_DEFAULT_FRAGMENT_SHADER = R"(#version 330
 in vec4 color;
 in vec2 uvCoord;
-out vec4 outputColor;
+
+out vec4 fragColor;
+
+void main()
+{
+    vec2 p = -1.0 + 2.0 * uvCoord;
+    float l = sqrt(dot(p, p));
+    l = 1.0 - clamp(l, 0.0, 1.0);
+    l *= color.a;
+
+    fragColor = vec4(color.rgb, l);
+}
+
+)";
+
+  const static std::string PARTICLE_SOLID_FRAGMENT_SHADER = R"(#version 330
+in vec4 color;
+in vec2 uvCoord;
+
+layout (location = 0) out vec4 accumulation;
+layout (location = 1) out float reveal;
+
 void main()
 {
   vec2 p = -1.0 + 2.0 * uvCoord;
@@ -146,10 +176,37 @@ void main()
   if( l > 0.8 )
     discard;
 
-  outputColor = vec4(color.rgb, 1.0f);
+  vec4 c = vec4(color.rgb, 1.0f);
+
+  float weight = clamp(pow(min(1.0, c.a * 10.0) + 0.01, 3.0) * 1e8
+  * pow(1.0 - gl_FragCoord.z * 0.9, 3.0), 1e-2, 3e3);
+
+  accumulation = vec4 (c.rgb * c.a, c.a) * weight;
+  reveal = c.a;
 })";
 
-const static std::string PICK_VERTEX_SHADER = R"(#version 400
+  const static std::string PARTICLE_ACC_SOLID_FRAGMENT_SHADER = R"(#version 330
+in vec4 color;
+in vec2 uvCoord;
+
+out vec4 fragColor;
+
+void main()
+{
+    vec2 p = -1.0 + 2.0 * uvCoord;
+    float l = sqrt(dot(p,p));
+    l = clamp(l, 0.0, 1.0);
+
+    if( l > 0.8 )
+      discard;
+
+    fragColor = vec4(color.rgb, 1.0f);
+}
+
+)";
+
+
+  const static std::string PICK_VERTEX_SHADER = R"(#version 330
 #extension GL_ARB_separate_shader_objects: enable
 
 uniform mat4 modelViewProjM;
@@ -189,7 +246,7 @@ void main()
   id = gl_InstanceID;
 })";
 
-const static std::string PICK_FRAGMENT_SHADER = R"(#version 400
+  const static std::string PICK_FRAGMENT_SHADER = R"(#version 330
 
 uniform float radiusThreshold;
 
@@ -222,8 +279,7 @@ void main( )
   outputColor = vec4(cc, 1.0);
 })";
 
-const static std::string SHADER_PLANE_VERTEX = R"(
-#version 400
+  const static std::string SHADER_PLANE_VERTEX = R"(#version 330
 
 in vec3 inPos;
 
@@ -243,8 +299,7 @@ void main( )
 )";
 
 
-const static std::string SHADER_PLANE_FRAGMENT = R"(
-#version 400
+  const static std::string SHADER_PLANE_FRAGMENT = R"(#version 330
 
 in vec4 outColor;
 out vec4 outputColor;
@@ -256,6 +311,71 @@ void main( )
   
 }
 
+)";
+
+
+  const static std::string SHADER_SCREEN_VERTEX = R"(#version 330
+
+// shader inputs
+layout (location = 0) in vec3 position;
+
+void main()
+{
+	gl_Position = vec4(position, 1.0f);
+}
+)";
+
+  const static std::string SHADER_SCREEN_FRAGMENT = R"(#version 330
+
+// shader outputs
+layout (location = 0) out vec4 frag;
+
+// color accumulation buffer
+uniform sampler2D accumulation;
+
+// revealage threshold buffer
+uniform sampler2D reveal;
+
+// epsilon number
+const float EPSILON = 0.00001f;
+
+// caluclate floating point numbers equality accurately
+bool isApproximatelyEqual(float a, float b)
+{
+	return abs(a - b) <= (abs(a) < abs(b) ? abs(b) : abs(a)) * EPSILON;
+}
+
+// get the max value between three values
+float max3(vec3 v)
+{
+	return max(max(v.x, v.y), v.z);
+}
+
+void main()
+{
+	// fragment coordination
+	ivec2 coords = ivec2(gl_FragCoord.xy);
+
+	// fragment revealage
+	float revealage = texelFetch(reveal, coords, 0).r;
+
+	// save the blending and color texture fetch cost if there is not a transparent fragment
+	if (isApproximatelyEqual(revealage, 1.0f))
+		discard;
+
+	// fragment color
+	vec4 accumulation = texelFetch(accumulation, coords, 0);
+
+	// suppress overflow
+	if (isinf(max3(abs(accumulation.rgb))))
+		accumulation.rgb = vec3(accumulation.a);
+
+	// prevent floating point precision bug
+	vec3 average_color = accumulation.rgb / max(accumulation.a, EPSILON);
+
+	// blend pixels
+	frag = vec4(average_color, 1.0f - revealage);
+}
 )";
 
 }