From 94cb1c8028f1fd838b89395b50ecabd649adde29 Mon Sep 17 00:00:00 2001
From: Mathias Paulin <mathias.paulin@irit.fr>
Date: Thu, 11 Mar 2021 22:25:55 +0100
Subject: [PATCH] Separate NodeBasedRenderer interface from
 FullFeaturedRenderer implementation.

---
 src/Mara/RadiumPlayer.cpp                     |   6 +-
 src/Plugin/NodeRendererPlugin.cpp             |   6 +-
 src/Plugin/NodeRendererPlugin.hpp             |   4 +-
 src/libRender/CMakeLists.txt                  |   6 +-
 .../RadiumNBR/FullFeatureRenderer.cpp         | 370 ++++++++++++++++
 .../RadiumNBR/FullFeatureRenderer.hpp         | 120 +++++
 ...rerGui.cpp => FullFeaturedRendererGui.cpp} |   6 +-
 ...rerGui.hpp => FullFeaturedRendererGui.hpp} |   4 +-
 src/libRender/RadiumNBR/NodeBasedRenderer.cpp | 409 +++---------------
 src/libRender/RadiumNBR/NodeBasedRenderer.hpp | 163 +++----
 src/libRender/RadiumNBR/RenderPass.hpp        |   2 +
 11 files changed, 624 insertions(+), 472 deletions(-)
 create mode 100644 src/libRender/RadiumNBR/FullFeatureRenderer.cpp
 create mode 100644 src/libRender/RadiumNBR/FullFeatureRenderer.hpp
 rename src/libRender/RadiumNBR/Gui/{NodeBasedRendererGui.cpp => FullFeaturedRendererGui.cpp} (92%)
 rename src/libRender/RadiumNBR/Gui/{NodeBasedRendererGui.hpp => FullFeaturedRendererGui.hpp} (62%)

diff --git a/src/Mara/RadiumPlayer.cpp b/src/Mara/RadiumPlayer.cpp
index d31010d..7206426 100644
--- a/src/Mara/RadiumPlayer.cpp
+++ b/src/Mara/RadiumPlayer.cpp
@@ -14,8 +14,8 @@ using namespace Ra::Core::Utils; // for LOG( logLEVEL )
 #include <QCommandLineParser>
 #include <QFileDialog>
 
-#include <RadiumNBR/Gui/NodeBasedRendererGui.hpp>
-#include <RadiumNBR/NodeBasedRenderer.hpp>
+#include <RadiumNBR/FullFeatureRenderer.hpp>
+#include <RadiumNBR/Gui/FullFeaturedRendererGui.hpp>
 
 #ifdef WITH_H3D_SUPPORT
 #    include <RadiumH3D/h3DLoader.hpp>
@@ -135,7 +135,7 @@ void RadiumPlayer::addRenderers() {
     }
     // add experimental renderer
     {
-        auto myRenderer          = std::make_shared<RadiumNBR::NodeBasedRenderer>();
+        auto myRenderer          = std::make_shared<RadiumNBR::FullFeatureRenderer>();
         std::string rendererName = myRenderer->getRendererName();
         auto controlPanel =
             RadiumNBR::buildRadiumNBRGui( myRenderer.get(), [this]() { this->askForUpdate(); } );
diff --git a/src/Plugin/NodeRendererPlugin.cpp b/src/Plugin/NodeRendererPlugin.cpp
index 5798608..c9f3760 100644
--- a/src/Plugin/NodeRendererPlugin.cpp
+++ b/src/Plugin/NodeRendererPlugin.cpp
@@ -1,6 +1,6 @@
 #include <NodeRendererPlugin.hpp>
 
-#include <RadiumNBR/Gui/NodeBasedRendererGui.hpp>
+#include <RadiumNBR/Gui/FullFeaturedRendererGui.hpp>
 
 namespace RadiumNBRPlugin {
 
@@ -15,7 +15,7 @@ bool NodeRendererPlugin::doAddWidget( QString& name ) {
 }
 
 QWidget* NodeRendererPlugin::getWidget() {
-    if ( !m_renderer ) { m_renderer = std::make_shared<RadiumNBR::NodeBasedRenderer>(); }
+    if ( !m_renderer ) { m_renderer = std::make_shared<RadiumNBR::FullFeatureRenderer>(); }
     auto sendSignal = [this]() { emit askForUpdate(); };
     return RadiumNBR::buildRadiumNBRGui( m_renderer.get(), sendSignal );
 }
@@ -44,7 +44,7 @@ bool NodeRendererPlugin::doAddRenderer() {
 }
 void NodeRendererPlugin::addRenderers(
     std::vector<std::shared_ptr<Ra::Engine::Rendering::Renderer>>* rds ) {
-    if ( !m_renderer ) { m_renderer = std::make_shared<RadiumNBR::NodeBasedRenderer>(); }
+    if ( !m_renderer ) { m_renderer = std::make_shared<RadiumNBR::FullFeatureRenderer>(); }
     rds->push_back( m_renderer );
 }
 
diff --git a/src/Plugin/NodeRendererPlugin.hpp b/src/Plugin/NodeRendererPlugin.hpp
index a7ff67e..2fc506e 100644
--- a/src/Plugin/NodeRendererPlugin.hpp
+++ b/src/Plugin/NodeRendererPlugin.hpp
@@ -5,7 +5,7 @@
 
 #include <PluginBase/RadiumPluginInterface.hpp>
 
-#include <RadiumNBR/NodeBasedRenderer.hpp>
+#include <RadiumNBR/FullFeatureRenderer.hpp>
 
 namespace RadiumNBRPlugin {
 
@@ -45,7 +45,7 @@ class NodeRenderer_PLUGIN_API NodeRendererPlugin : public QObject,
     void askForUpdate();
 
   private:
-    std::shared_ptr<RadiumNBR::NodeBasedRenderer> m_renderer;
+    std::shared_ptr<RadiumNBR::FullFeatureRenderer> m_renderer;
 };
 
 } // namespace RadiumNBRPlugin
diff --git a/src/libRender/CMakeLists.txt b/src/libRender/CMakeLists.txt
index 33683c5..ce0a3be 100644
--- a/src/libRender/CMakeLists.txt
+++ b/src/libRender/CMakeLists.txt
@@ -31,6 +31,7 @@ set(markdowns
 set(sources
     RadiumNBR/NodeBasedRenderer.cpp
     RadiumNBR/EnvMap.cpp
+    RadiumNBR/FullFeatureRenderer.cpp
     RadiumNBR/SphereSampler.cpp
     RadiumNBR/Passes/ClearPass.cpp
     RadiumNBR/Passes/GeomPrepass.cpp
@@ -46,6 +47,7 @@ set(public_headers
     RadiumNBR/NodeBasedRendererMacro.hpp
     RadiumNBR/NodeBasedRenderer.hpp
     RadiumNBR/EnvMap.hpp
+    RadiumNBR/FullFeatureRenderer.hpp
     RadiumNBR/RenderPass.hpp
     RadiumNBR/SphereSampler.hpp
     RadiumNBR/Passes/ClearPass.hpp
@@ -122,12 +124,12 @@ set(gui_markdowns
     )
 
 set(gui_sources
-    RadiumNBR/Gui/NodeBasedRendererGui.cpp
+    RadiumNBR/Gui/FullFeaturedRendererGui.cpp
     RadiumNBR/Gui/RendererPanel.cpp
     )
 
 set(gui_public_headers
-    RadiumNBR/Gui/NodeBasedRendererGui.hpp
+    RadiumNBR/Gui/FullFeaturedRendererGui.hpp
     RadiumNBR/Gui/RendererPanel.hpp
     )
 
diff --git a/src/libRender/RadiumNBR/FullFeatureRenderer.cpp b/src/libRender/RadiumNBR/FullFeatureRenderer.cpp
new file mode 100644
index 0000000..f455a4e
--- /dev/null
+++ b/src/libRender/RadiumNBR/FullFeatureRenderer.cpp
@@ -0,0 +1,370 @@
+#include <RadiumNBR/FullFeatureRenderer.hpp>
+
+#include <Core/Containers/MakeShared.hpp>
+#include <Core/Resources/Resources.hpp>
+
+#ifdef PASSES_LOG
+#    include <Core/Utils/Log.hpp>
+using namespace Ra::Core::Utils; // log
+#endif
+
+#include <Engine/Data/Material.hpp>
+#include <Engine/Data/ShaderProgramManager.hpp>
+#include <Engine/Rendering/RenderObject.hpp>
+#include <Engine/Scene/DefaultCameraManager.hpp>
+#include <Engine/Scene/DefaultLightManager.hpp>
+
+#include <RadiumNBR/Passes/AccessibilityBufferPass.hpp>
+#include <RadiumNBR/Passes/ClearPass.hpp>
+#include <RadiumNBR/Passes/EmissivityPass.hpp>
+#include <RadiumNBR/Passes/EnvLightPass.hpp>
+#include <RadiumNBR/Passes/GeomPrepass.hpp>
+#include <RadiumNBR/Passes/LocalLightPass.hpp>
+#include <RadiumNBR/Passes/TransparencyPass.hpp>
+#include <RadiumNBR/Passes/VolumePass.hpp>
+#include <RadiumNBR/Passes/WireframePass.hpp>
+
+#include <globjects/Framebuffer.h>
+
+using namespace Ra::Engine;
+using namespace Ra::Engine::Scene;
+using namespace Ra::Engine::Data;
+using namespace Ra::Engine::Rendering;
+
+namespace RadiumNBR {
+using namespace gl;
+
+static int FullFeaturedRendererMagic = 0x0F0F0F0F;
+
+static const GLenum buffers[] = { GL_COLOR_ATTACHMENT0,
+                                  GL_COLOR_ATTACHMENT1,
+                                  GL_COLOR_ATTACHMENT2,
+                                  GL_COLOR_ATTACHMENT3,
+                                  GL_COLOR_ATTACHMENT4,
+                                  GL_COLOR_ATTACHMENT5,
+                                  GL_COLOR_ATTACHMENT6,
+                                  GL_COLOR_ATTACHMENT7 };
+
+FullFeatureRenderer::FullFeatureRenderer() : NodeBasedRenderer() {}
+
+FullFeatureRenderer::~FullFeatureRenderer() = default;
+
+
+
+void FullFeatureRenderer::initializeInternal() {
+    NodeBasedRenderer::initializeInternal();
+    initPasses();
+    for ( const auto& t : sharedTextures() )
+    { m_secondaryTextures.insert( { t.first, t.second.get() } ); }
+}
+
+void FullFeatureRenderer::initPasses() {
+    auto resourcesCheck = Ra::Core::Resources::getResourcesPath(
+        reinterpret_cast<void*>( &FullFeaturedRendererMagic ), { "Resources/RadiumNBR" } );
+    if ( !resourcesCheck )
+    {
+        LOG( Ra::Core::Utils::logERROR ) << "Unable to find resources for NodeBasedRenderer!";
+        return;
+    }
+    auto resourcesPath{ *resourcesCheck };
+    auto depthTexture = sharedTextures().find( "Depth (RadiumNBR)" );
+    auto colorTexture = sharedTextures().find( "Linear RGB (RadiumNBR)" );
+
+    /***
+     * 0 - clear the final picture
+     *  Set the background color either to the global background color or to the env-map
+     */
+    {
+        m_clearPass = std::make_shared<ClearPass>( nullptr, FullFeaturedRendererPasses::CLEAR_PASS );
+        m_clearPass->setOutput( *colorTexture );
+        m_clearPass->setBackground( getBackgroundColor() );
+        m_clearPass->initializePass( m_width, m_height, m_shaderProgramManager );
+    }
+
+    /***
+     * 1 Extract basic geometric information : position, normal and depth
+     *  Only draw ont AOV Position and Normal
+     */
+    {
+        // Zprepass takes all objects but transparent objects are expected to render only their
+        // opaque fragments.
+        m_zPrePass = std::make_shared<GeomPrePass>( allRenderObjects(),
+                                                    FullFeaturedRendererPasses::Z_PASS );
+        // Add the shared depth texture
+        m_zPrePass->setOutput( *depthTexture );
+        // configure acces to shader files
+        m_zPrePass->setResourcesDir( resourcesPath );
+        m_zPrePass->initializePass( m_width, m_height, m_shaderProgramManager );
+        for ( auto&& t : m_zPrePass->getOutputImages() )
+        {
+            sharedTextures().insert( t );
+        }
+    }
+
+    /***
+     * Compute accessibiity buffer (ambient occlusion) from output of pass 1
+     *  Screen space ambiant occlusion
+     */
+    {
+        auto sphereSampler =
+            std::make_unique<SphereSampler>( m_aoSamplingMethod, m_aoSamplingPoints );
+        m_aoPass = std::make_shared<AccessibilityBufferPass>(
+            allRenderObjects(), FullFeaturedRendererPasses::ACCESSIBILITY_PASS );
+        m_aoPass->setResourcesDir( resourcesPath );
+        m_aoPass->setSampler( std::move( sphereSampler ) );
+        // Connect to the preceeding pass
+        auto posTexture = sharedTextures().find( "GeomPrePass::PosInWorld" );
+        auto nrmTexture = sharedTextures().find( "GeomPrePass::NormalInWorld" );
+        m_aoPass->setInputs( *posTexture, *nrmTexture );
+        m_aoPass->initializePass( m_width, m_height, m_shaderProgramManager );
+        for ( auto&& t : m_aoPass->getOutputImages() )
+        {
+            sharedTextures().insert( t );
+        }
+    }
+    auto ambientOcclusionTexture = sharedTextures().find( "SSDO::AOBuffer" );
+    /***
+     * 3 - Render emissivity
+     *  Render Ambiant or emissivity color.
+     *      Ambiant is 0.01 the base color. TODO -> set 0.01 a parameter of the pass
+     *      Do not blend with output, replace the background color by the emissivity/ambiant color
+     */
+    {
+        m_emissivityPass = std::make_shared<EmissivityPass>(
+            allRenderObjects(), FullFeaturedRendererPasses::EMISSIVITY_PASS );
+        m_emissivityPass->setResourcesDir( resourcesPath );
+        m_emissivityPass->setInputs( *depthTexture, *ambientOcclusionTexture );
+        m_emissivityPass->setOutput( *colorTexture );
+        m_emissivityPass->setBackground( getBackgroundColor() );
+        m_emissivityPass->initializePass( m_width, m_height, m_shaderProgramManager );
+    }
+    /***
+     * 4 - Render envlighting
+     *      Only render scene if an envmap is set
+     *      Blend with the previous color buffer
+     */
+    {
+        m_envlightPass = std::make_shared<EnvLightPass>(
+            allRenderObjects(), FullFeaturedRendererPasses::ENVMAP_LIGHTING_OPAQUE_PASS );
+        m_envlightPass->setResourcesDir( resourcesPath );
+        m_envlightPass->setInputs( *depthTexture, *ambientOcclusionTexture );
+        m_envlightPass->setOutput( *colorTexture );
+        m_envlightPass->initializePass( m_width, m_height, m_shaderProgramManager );
+    }
+
+    /***
+     * 5 - Compute lighting - Opaque objects
+     */
+    {
+        m_locallightPass = std::make_shared<LocalLightPass>(
+            allRenderObjects(), FullFeaturedRendererPasses::LIGHTING_OPAQUE_PASS );
+        m_locallightPass->setResourcesDir( resourcesPath );
+        m_locallightPass->setInputs( *depthTexture, *ambientOcclusionTexture );
+        m_locallightPass->setOutput( *colorTexture );
+        m_locallightPass->setLightManager( m_lightmanagers[0] );
+        m_locallightPass->initializePass( m_width, m_height, m_shaderProgramManager );
+    }
+    /***
+     * 6 - Compute lighting - Transparent objects
+     */
+    {
+        m_transparencyPass = std::make_shared<TransparencyPass>(
+            &m_transparentRenderObjects, FullFeaturedRendererPasses::LIGHTING_TRANSPARENT_PASS );
+        m_transparencyPass->setResourcesDir( resourcesPath );
+        m_transparencyPass->setInputs( *depthTexture, *ambientOcclusionTexture );
+        m_transparencyPass->setOutput( *colorTexture );
+        m_transparencyPass->setLightManager( m_lightmanagers[0] );
+        m_transparencyPass->initializePass( m_width, m_height, m_shaderProgramManager );
+    }
+
+    /***
+     * 7 - Compute lighting for volume objects
+     */
+    {
+        m_volumelightPass = std::make_shared<VolumeLightingPass>(
+            &m_volumetricRenderObjects, FullFeaturedRendererPasses::LIGHTING_VOLUME_PASS );
+        m_volumelightPass->setResourcesDir( resourcesPath );
+        m_volumelightPass->setInputs( *depthTexture, *colorTexture );
+        m_volumelightPass->setOutput( *colorTexture );
+        m_volumelightPass->setLightManager( m_lightmanagers[0] );
+        m_volumelightPass->initializePass( m_width, m_height, m_shaderProgramManager );
+    }
+
+    /***
+     * Option - Render in wireframe
+     *  Render all objects as wireframe
+     *      Render in white over an existing depth buffer
+     */
+    {
+        m_wireframePass = std::make_shared<WireframePass>(
+            allRenderObjects(), FullFeaturedRendererPasses::WIREFRAME_PASS );
+        m_wireframePass->setResourcesDir( resourcesPath );
+        m_wireframePass->setInputs( *depthTexture );
+        m_wireframePass->setOutput( *colorTexture );
+        m_wireframePass->initializePass( m_width, m_height, m_shaderProgramManager );
+    }
+
+
+    // Build the sequence of active passes
+    addPass(m_clearPass, m_clearPass->index());
+    m_clearPass->activate();
+    addPass(m_zPrePass, m_zPrePass->index());
+    m_zPrePass->activate();
+    addPass( m_aoPass, m_aoPass->index() );
+    m_aoPass->activate();
+
+    addPass( m_emissivityPass, m_emissivityPass->index() );
+    m_emissivityPass->activate();
+
+    addPass( m_envlightPass, m_envlightPass->index() );
+    m_envlightPass->activate();
+
+    addPass( m_locallightPass, m_locallightPass->index(), true );
+    m_locallightPass->activate();
+    addPass( m_transparencyPass, m_transparencyPass->index() );
+    m_transparencyPass->activate();
+    addPass( m_volumelightPass, m_volumelightPass->index() );
+    m_volumelightPass->activate();
+
+    addPass( m_wireframePass, m_wireframePass->index() );
+
+    // Add intermediate textures to the texture explorer
+    for ( const auto& p : renderPasses() )
+    {
+        for ( auto&& t : p.second->getOutputImages() )
+        {
+            sharedTextures().insert( t );
+        }
+    }
+}
+
+
+// Todo : verify if the ro partition is the good one and that the passes use the right part.
+void FullFeatureRenderer::updateStepInternal( const ViewingParameters& renderData ) {
+    // Split objects into opaque and transparent
+    m_transparentRenderObjects.clear();
+    m_volumetricRenderObjects.clear();
+    allRenderObjects()->clear();
+    for ( auto it = m_fancyRenderObjects.begin(); it != m_fancyRenderObjects.end(); )
+    {
+        allRenderObjects()->push_back( *it );
+        if ( ( *it )->isTransparent() )
+        {
+            m_transparentRenderObjects.push_back( *it );
+            it = m_fancyRenderObjects.erase( it );
+        }
+        else
+        {
+            auto material = ( *it )->getMaterial();
+            if ( material &&
+                 material->getMaterialAspect() == Material::MaterialAspect::MAT_DENSITY )
+            {
+                m_volumetricRenderObjects.push_back( *it );
+                it = m_fancyRenderObjects.erase( it );
+            }
+            else
+            { ++it; }
+        }
+    }
+
+    // Update passes
+    // Note that this could be expensive. Find a way to add attributes to renderObjects so
+    // that one ca factorizes some evaluations.
+    for ( auto& rp : renderPasses() )
+    {
+        if ( rp.second->isActive() ) { rp.second->update(); }
+    }
+}
+
+void FullFeatureRenderer::renderInternal( const ViewingParameters& renderData ) {
+    m_clearPass->setBackground( getBackgroundColor() );
+    if ( m_hasEnvMap )
+    {
+        // for ( auto &lm : m_lightmanagers ) { lm->removeSystemLights(); }
+    }
+    NodeBasedRenderer::renderInternal( renderData );
+}
+
+
+
+void FullFeatureRenderer::setEnvMap( const std::string& files ) {
+    if ( files.empty() )
+    {
+        m_clearPass->setEnvMap( nullptr );
+        m_envlightPass->setEnvMap( nullptr );
+        m_envlightPass->deactivate();
+        m_volumelightPass->setEnvMap( nullptr );
+        m_hasEnvMap = false;
+    }
+    else
+    {
+        // Todo, add type VERTICALCROSS, HORIZONTALCROSS and differentiate between all types
+        //  using file extension and file size
+        auto t = ( files.find( ';' ) != files.npos ) ? EnvMap::EnvMapType::ENVMAP_CUBE
+                                                     : EnvMap::EnvMapType::ENVMAP_PFM;
+        if ( t == EnvMap::EnvMapType::ENVMAP_PFM )
+        {
+            auto ext = files.substr( files.size() - 3 );
+            if ( ext != "pfm" ) { t = EnvMap::EnvMapType::ENVMAP_LATLON; }
+        }
+        // for now, only skyboxes are managed
+        auto e = std::make_shared<EnvMap>( files, t, true );
+        m_clearPass->setEnvMap( e );
+        m_envlightPass->setEnvMap( e );
+        // activate the envmap pass only if wireframe is inactive ?
+        if ( !m_wireframePass->isActive() ) { m_envlightPass->activate(); }
+        m_volumelightPass->setEnvMap( e );
+        m_hasEnvMap = true;
+    }
+}
+
+void FullFeatureRenderer::showEnvMap( bool state ) {
+    m_clearPass->showEnvMap( state );
+}
+
+void FullFeatureRenderer::setEnvStrength( int s ) {
+    m_envlightPass->setEnvStrength( s );
+}
+
+Scalar FullFeatureRenderer::getAoRadius() const {
+    if ( m_aoPass ) { return m_aoPass->aoRadius(); }
+    return 5.0;
+}
+void FullFeatureRenderer::setAoRadius( Scalar r ) {
+    if ( m_aoPass ) { m_aoPass->setAoRadius( r ); }
+}
+
+int FullFeatureRenderer::getAoSamplingDensity() const {
+    return m_aoSamplingPoints;
+}
+void FullFeatureRenderer::setAoSamplingDensity( int d ) {
+    m_aoSamplingPoints = d;
+    auto sphereSampler = std::make_unique<SphereSampler>( m_aoSamplingMethod, m_aoSamplingPoints );
+    if ( m_aoPass ) { m_aoPass->setSampler( std::move( sphereSampler ) ); }
+}
+
+void FullFeatureRenderer::wireframeMode( bool status ) {
+    enableWireframe( status );
+    if ( m_wireframe ) {
+        m_wireframePass->activate();
+
+        m_aoPass->deactivate();
+        m_emissivityPass->deactivate();
+        m_envlightPass->deactivate();
+        m_locallightPass->deactivate();
+        m_transparencyPass->deactivate();
+        m_volumelightPass->deactivate();
+
+    } else {
+        m_wireframePass->deactivate();
+
+        m_aoPass->activate();
+        m_emissivityPass->activate();
+        m_envlightPass->activate();
+        m_locallightPass->activate();
+        m_transparencyPass->activate();
+        m_volumelightPass->activate();
+
+    }
+}
+} // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/FullFeatureRenderer.hpp b/src/libRender/RadiumNBR/FullFeatureRenderer.hpp
new file mode 100644
index 0000000..44dba4f
--- /dev/null
+++ b/src/libRender/RadiumNBR/FullFeatureRenderer.hpp
@@ -0,0 +1,120 @@
+#pragma once
+#include <RadiumNBR/NodeBasedRendererMacro.hpp>
+#include <RadiumNBR/RenderPass.hpp>
+#include <RadiumNBR/SphereSampler.hpp>
+
+#include <RadiumNBR/NodeBasedRenderer.hpp>
+
+namespace globjects {
+class Framebuffer;
+}
+
+namespace Ra::Engine::Data {
+class Texture;
+} // namespace Ra::Engine::Data
+
+namespace RadiumNBR {
+class ClearPass;
+class GeomPrePass;
+class AccessibilityBufferPass;
+class EmissivityPass;
+class EnvLightPass;
+class LocalLightPass;
+class VolumeLightingPass;
+class TransparencyPass;
+class WireframePass;
+
+/** Advanced renderer for the Radium Engine
+ * This class implements a forward rendering algorithm with Z-prepass, multipass light accumulation
+ * for opaque and transparent objects.
+ * Once renderer, the final is composited with Ui, debug and
+ * X-ray objects renderings on demand.
+ *
+ * @see rendering.md for description of the renderer
+ */
+class NodeBasedRenderer_LIBRARY_API FullFeatureRenderer final : public NodeBasedRenderer
+{
+
+  public:
+    FullFeatureRenderer();
+    ~FullFeatureRenderer() override;
+
+    [[nodiscard]] std::string getRendererName() const override { return "Full Featured Renderer"; }
+
+    void setEnvMap( const std::string& files );
+    void showEnvMap( bool state );
+    // TODO : define a way to configfure each passes without adding here specifi methods
+    // TODO the following might be removed in the future
+    Scalar getAoRadius() const;
+    void setAoRadius( Scalar r );
+    int getAoSamplingDensity() const;
+    void setAoSamplingDensity( int d );
+    void setEnvStrength( int s );
+    void wireframeMode( bool status );
+
+  protected:
+    void initializeInternal() override;
+    void updateStepInternal( const Ra::Engine::Data::ViewingParameters& renderData ) override;
+    void renderInternal( const Ra::Engine::Data::ViewingParameters& renderData ) override;
+
+  private:
+    void initPasses();
+
+    enum FullFeaturedRendererPasses : int {
+        CLEAR_PASS = 0,
+        Z_PASS,
+        ACCESSIBILITY_PASS,
+        EMISSIVITY_PASS,
+        ENVMAP_LIGHTING_OPAQUE_PASS,
+        LIGHTING_OPAQUE_PASS,
+        LIGHTING_TRANSPARENT_PASS,
+        LIGHTING_VOLUME_PASS,
+        WIREFRAME_PASS,
+        NUM_PASSES,
+        DEFAULT_PASS = LIGHTING_OPAQUE_PASS
+    };
+  protected:
+
+    /// Subset of the objects that are transparent and need special rendering
+    std::vector<RenderObjectPtr> m_transparentRenderObjects;
+
+    /// Subset of the objects that are volumetric
+    std::vector<RenderObjectPtr> m_volumetricRenderObjects;
+
+    /// The ambiant occlusion pass
+    std::shared_ptr<AccessibilityBufferPass> m_aoPass;
+    // The sampling method of the sphere sampler
+    SphereSampler::SamplingMethod m_aoSamplingMethod{ SphereSampler::SamplingMethod::HAMMERSLEY };
+    // The number of points for the sampler
+    int m_aoSamplingPoints{ 64 };
+
+    /// clear the final ouput image
+    std::shared_ptr<ClearPass> m_clearPass;
+
+    /// The zprepass
+    std::shared_ptr<GeomPrePass> m_zPrePass;
+
+    /// The emissivity  pass
+    std::shared_ptr<EmissivityPass> m_emissivityPass;
+
+    /// The envlight  pass
+    std::shared_ptr<EnvLightPass> m_envlightPass;
+
+    /// The local lighting pass
+    std::shared_ptr<LocalLightPass> m_locallightPass;
+
+    /// The volume lighting pass
+    std::shared_ptr<VolumeLightingPass> m_volumelightPass;
+
+    /// The transparency (Order independant transparency) pass
+    std::shared_ptr<TransparencyPass> m_transparencyPass;
+
+    /// The transparency (Order independant transparency) pass
+    std::shared_ptr<WireframePass> m_wireframePass;
+
+    /// Is an envmap attached to the renderer
+    bool m_hasEnvMap{ false };
+
+};
+
+} // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/Gui/NodeBasedRendererGui.cpp b/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.cpp
similarity index 92%
rename from src/libRender/RadiumNBR/Gui/NodeBasedRendererGui.cpp
rename to src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.cpp
index 7645bd1..625125a 100644
--- a/src/libRender/RadiumNBR/Gui/NodeBasedRendererGui.cpp
+++ b/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.cpp
@@ -1,10 +1,10 @@
-#include <RadiumNBR/Gui/NodeBasedRendererGui.hpp>
-#include <RadiumNBR/NodeBasedRenderer.hpp>
+#include <RadiumNBR/FullFeatureRenderer.hpp>
+#include <RadiumNBR/Gui/FullFeaturedRendererGui.hpp>
 
 namespace RadiumNBR {
 using namespace Gui;
 
-RadiumNBR::Gui::RendererPanel* buildRadiumNBRGui( NodeBasedRenderer* renderer,
+RadiumNBR::Gui::RendererPanel* buildRadiumNBRGui( FullFeatureRenderer* renderer,
                                                   const std::function<void()>& appUpdateCallback ) {
     auto controlPanel = new RendererPanel( renderer->getRendererName() );
 
diff --git a/src/libRender/RadiumNBR/Gui/NodeBasedRendererGui.hpp b/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.hpp
similarity index 62%
rename from src/libRender/RadiumNBR/Gui/NodeBasedRendererGui.hpp
rename to src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.hpp
index 0a0de26..ccfd7c5 100644
--- a/src/libRender/RadiumNBR/Gui/NodeBasedRendererGui.hpp
+++ b/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.hpp
@@ -3,9 +3,9 @@
 #include <RadiumNBR/NodeBasedRendererMacro.hpp>
 
 namespace RadiumNBR {
-class NodeBasedRenderer;
+class FullFeatureRenderer;
 
 NodeBasedRenderer_LIBRARY_API RadiumNBR::Gui::RendererPanel*
-buildRadiumNBRGui( NodeBasedRenderer* renderer, const std::function<void()>& appUpdateCallback );
+buildRadiumNBRGui( FullFeatureRenderer* renderer, const std::function<void()>& appUpdateCallback );
 
 } // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/NodeBasedRenderer.cpp b/src/libRender/RadiumNBR/NodeBasedRenderer.cpp
index fd28a8a..a6e4f71 100644
--- a/src/libRender/RadiumNBR/NodeBasedRenderer.cpp
+++ b/src/libRender/RadiumNBR/NodeBasedRenderer.cpp
@@ -1,50 +1,30 @@
 #include <RadiumNBR/NodeBasedRenderer.hpp>
 
 #include <Core/Containers/MakeShared.hpp>
-#include <Core/Resources/Resources.hpp>
 
 #ifdef PASSES_LOG
 #    include <Core/Utils/Log.hpp>
 using namespace Ra::Core::Utils; // log
 #endif
 
-#include <Engine/Data/Material.hpp>
 #include <Engine/Data/ShaderProgramManager.hpp>
 #include <Engine/Data/Texture.hpp>
 #include <Engine/Rendering/RenderObject.hpp>
 #include <Engine/Scene/DefaultCameraManager.hpp>
 #include <Engine/Scene/DefaultLightManager.hpp>
 
-#include <RadiumNBR/Passes/AccessibilityBufferPass.hpp>
-#include <RadiumNBR/Passes/ClearPass.hpp>
-#include <RadiumNBR/Passes/EmissivityPass.hpp>
-#include <RadiumNBR/Passes/EnvLightPass.hpp>
-#include <RadiumNBR/Passes/GeomPrepass.hpp>
-#include <RadiumNBR/Passes/LocalLightPass.hpp>
-#include <RadiumNBR/Passes/TransparencyPass.hpp>
-#include <RadiumNBR/Passes/VolumePass.hpp>
-#include <RadiumNBR/Passes/WireframePass.hpp>
-
 #include <globjects/Framebuffer.h>
 
 using namespace Ra::Engine;
 using namespace Ra::Engine::Scene;
 using namespace Ra::Engine::Data;
 using namespace Ra::Engine::Rendering;
-
 namespace RadiumNBR {
 using namespace gl;
 
-static int NodeBasedRendererMagic = 0x0F0F0F0F;
+static int NodeBasedRendererMagic = 0xFF0F00F0;
 
-static const GLenum buffers[] = { GL_COLOR_ATTACHMENT0,
-                                  GL_COLOR_ATTACHMENT1,
-                                  GL_COLOR_ATTACHMENT2,
-                                  GL_COLOR_ATTACHMENT3,
-                                  GL_COLOR_ATTACHMENT4,
-                                  GL_COLOR_ATTACHMENT5,
-                                  GL_COLOR_ATTACHMENT6,
-                                  GL_COLOR_ATTACHMENT7 };
+static const GLenum buffers[] = { GL_COLOR_ATTACHMENT0 };
 
 NodeBasedRenderer::NodeBasedRenderer() : Renderer() {}
 
@@ -52,234 +32,30 @@ NodeBasedRenderer::~NodeBasedRenderer() = default;
 
 bool NodeBasedRenderer::buildRenderTechnique( RenderObject* ro ) const {
     auto rt = Ra::Core::make_shared<RenderTechnique>();
-    for ( auto& rp : m_renderPasses )
+    for ( auto& rp : renderPasses() )
     {
-        rp->buildRenderTechnique( ro, *rt );
+        rp.second->buildRenderTechnique( ro, *rt );
     }
     rt->updateGL();
     ro->setRenderTechnique( rt );
     return true;
 }
 
-void NodeBasedRenderer::initializeInternal() {
-
-    // TODO : this must be done only once, see register system ...
-    if ( Ra::Engine::RadiumEngine::getInstance()->getSystem( "DefaultCameraManager" ) == nullptr )
-    {
-        auto cameraManager = new DefaultCameraManager();
-        Ra::Engine::RadiumEngine::getInstance()->registerSystem( "DefaultCameraManager",
-                                                                 cameraManager );
-    }
-    auto lmngr = dynamic_cast<DefaultLightManager*>(
-        Ra::Engine::RadiumEngine::getInstance()->getSystem( "DefaultLightManager" ) );
-    if ( lmngr == nullptr )
-    {
-        lmngr = new DefaultLightManager();
-        Ra::Engine::RadiumEngine::getInstance()->registerSystem( "DefaultLightManager", lmngr );
-    }
-    m_lightmanagers.push_back( lmngr );
-
-    // Initialize renderer resources
-    initShaders();
-    initBuffers();
-    initPasses();
-    for ( const auto& t : m_sharedTextures )
-    { m_secondaryTextures.insert( { t.first, t.second.get() } ); }
-}
-
-void NodeBasedRenderer::initPasses() {
-    auto resourcesCheck = Ra::Core::Resources::getResourcesPath(
-        reinterpret_cast<void*>( &NodeBasedRendererMagic ), { "Resources/RadiumNBR" } );
-    if ( !resourcesCheck )
-    {
-        LOG( Ra::Core::Utils::logERROR ) << "Unable to find resources for NodeBasedRenderer!";
-        return;
-    }
-    auto resourcesPath{ *resourcesCheck };
-    auto depthTexture = m_sharedTextures.find( "Depth ( AdvRndr )" );
-    auto colorTexture = m_sharedTextures.find( "Linear RGB ( AdvRndr )" );
-
-    /***
-     * 0 - clear the final picture
-     *  Set the background color either to the global background color or to the env-map
-     */
-    {
-        m_clearPass = std::make_shared<ClearPass>( nullptr, NodeBasedRendererPasses::CLEAR_PASS );
-        m_clearPass->setOutput( *colorTexture );
-        m_clearPass->setBackground( getBackgroundColor() );
-        m_clearPass->initializePass( m_width, m_height, m_shaderProgramManager );
-    }
-
-    /***
-     * 1 Extract basic geometric information : position, normal and depth
-     *  Only draw ont AOV Position and Normal
-     */
-    {
-        // Zprepass takes all objects but transparent objects are expected to render only their
-        // opaque fragments.
-        m_zPrePass = std::make_shared<GeomPrePass>( &m_allRenderObjects,
-                                                    NodeBasedRendererPasses::GEOM_PASS );
-        // Add the shared depth texture
-        m_zPrePass->setOutput( *depthTexture );
-        // configure acces to shader files
-        m_zPrePass->setResourcesDir( resourcesPath );
-        m_zPrePass->initializePass( m_width, m_height, m_shaderProgramManager );
-        for ( auto&& t : m_zPrePass->getOutputImages() )
-        {
-            m_sharedTextures.insert( t );
-        }
-    }
-
-    /***
-     * Compute accessibiity buffer (ambient occlusion) from output of pass 1
-     *  Screen space ambiant occlusion
-     */
-    {
-        auto sphereSampler =
-            std::make_unique<SphereSampler>( m_aoSamplingMethod, m_aoSamplingPoints );
-        m_aoPass = std::make_shared<AccessibilityBufferPass>(
-            &m_allRenderObjects, NodeBasedRendererPasses::ACCESSIBILITY_PASS );
-        m_aoPass->setResourcesDir( resourcesPath );
-        m_aoPass->setSampler( std::move( sphereSampler ) );
-        // Connect to the preceeding pass
-        auto posTexture = m_sharedTextures.find( "GeomPrePass::PosInWorld" );
-        auto nrmTexture = m_sharedTextures.find( "GeomPrePass::NormalInWorld" );
-        m_aoPass->setInputs( *posTexture, *nrmTexture );
-        m_aoPass->initializePass( m_width, m_height, m_shaderProgramManager );
-        for ( auto&& t : m_aoPass->getOutputImages() )
-        {
-            m_sharedTextures.insert( t );
-        }
-    }
-    auto ambientOcclusionTexture = m_sharedTextures.find( "SSDO::AOBuffer" );
-    /***
-     * 3 - Render emissivity
-     *  Render Ambiant or emissivity color.
-     *      Ambiant is 0.01 the base color. TODO -> set 0.01 a parameter of the pass
-     *      Do not blend with output, replace the background color by the emissivity/ambiant color
-     */
-    {
-        m_emissivityPass = std::make_shared<EmissivityPass>(
-            &m_allRenderObjects, NodeBasedRendererPasses::EMISSIVITY_PASS );
-        m_emissivityPass->setResourcesDir( resourcesPath );
-        m_emissivityPass->setInputs( *depthTexture, *ambientOcclusionTexture );
-        m_emissivityPass->setOutput( *colorTexture );
-        m_emissivityPass->setBackground( getBackgroundColor() );
-        m_emissivityPass->initializePass( m_width, m_height, m_shaderProgramManager );
-    }
-    /***
-     * 4 - Render envlighting
-     *      Only render scene if an envmap is set
-     *      Blend with the previous color buffer
-     */
-    {
-        m_envlightPass = std::make_shared<EnvLightPass>(
-            &m_allRenderObjects, NodeBasedRendererPasses::ENVMAP_LIGHTING_OPAQUE_PASS );
-        m_envlightPass->setResourcesDir( resourcesPath );
-        m_envlightPass->setInputs( *depthTexture, *ambientOcclusionTexture );
-        m_envlightPass->setOutput( *colorTexture );
-        m_envlightPass->initializePass( m_width, m_height, m_shaderProgramManager );
-    }
-
-    /***
-     * 5 - Compute lighting - Opaque objects
-     */
-    {
-        m_locallightPass = std::make_shared<LocalLightPass>(
-            &m_allRenderObjects, NodeBasedRendererPasses::LIGHTING_OPAQUE_PASS );
-        m_locallightPass->setResourcesDir( resourcesPath );
-        m_locallightPass->setInputs( *depthTexture, *ambientOcclusionTexture );
-        m_locallightPass->setOutput( *colorTexture );
-        m_locallightPass->setLightManager( m_lightmanagers[0] );
-        m_locallightPass->initializePass( m_width, m_height, m_shaderProgramManager );
-    }
-    /***
-     * 6 - Compute lighting - Transparent objects
-     */
-    {
-        m_transparencyPass = std::make_shared<TransparencyPass>(
-            &m_transparentRenderObjects, NodeBasedRendererPasses::LIGHTING_TRANSPARENT_PASS );
-        m_transparencyPass->setResourcesDir( resourcesPath );
-        m_transparencyPass->setInputs( *depthTexture, *ambientOcclusionTexture );
-        m_transparencyPass->setOutput( *colorTexture );
-        m_transparencyPass->setLightManager( m_lightmanagers[0] );
-        m_transparencyPass->initializePass( m_width, m_height, m_shaderProgramManager );
-    }
-
-    /***
-     * 7 - Compute lighting for volume objects
-     */
-    {
-        m_volumelightPass = std::make_shared<VolumeLightingPass>(
-            &m_volumetricRenderObjects, NodeBasedRendererPasses::LIGHTING_VOLUME_PASS );
-        m_volumelightPass->setResourcesDir( resourcesPath );
-        m_volumelightPass->setInputs( *depthTexture, *colorTexture );
-        m_volumelightPass->setOutput( *colorTexture );
-        m_volumelightPass->setLightManager( m_lightmanagers[0] );
-        m_volumelightPass->initializePass( m_width, m_height, m_shaderProgramManager );
-    }
-
-    /***
-     * Option - Render in wireframe
-     *  Render all objects as wireframe
-     *      Render in white over an existing depth buffer
-     */
-    {
-        m_wireframePass = std::make_shared<WireframePass>(
-            &m_allRenderObjects, NodeBasedRendererPasses::WIREFRAME_PASS );
-        m_wireframePass->setResourcesDir( resourcesPath );
-        m_wireframePass->setInputs( *depthTexture );
-        m_wireframePass->setOutput( *colorTexture );
-        m_wireframePass->initializePass( m_width, m_height, m_shaderProgramManager );
-    }
-
-
-    // Build the sequence of active passes
-    m_renderPasses.emplace_back( m_clearPass );
-    m_clearPass->activate();
-    m_renderPasses.emplace_back( m_zPrePass );
-    m_zPrePass->activate();
-    m_renderPasses.emplace_back( m_aoPass );
-    m_aoPass->activate();
-
-    m_renderPasses.emplace_back( m_emissivityPass );
-    m_emissivityPass->activate();
-
-    m_renderPasses.emplace_back( m_envlightPass );
-    m_envlightPass->activate();
-
-    m_renderPasses.emplace_back( m_locallightPass );
-    m_locallightPass->activate();
-    m_renderPasses.emplace_back( m_transparencyPass );
-    m_transparencyPass->activate();
-    m_renderPasses.emplace_back( m_volumelightPass );
-    m_volumelightPass->activate();
-
-    m_renderPasses.emplace_back( m_wireframePass );
-
-    // Add intermediate textures to the texture explorer
-    for ( const auto& p : m_renderPasses )
-    {
-        for ( auto&& t : p->getOutputImages() )
-        {
-            m_sharedTextures.insert( t );
-        }
-    }
-}
-
 void NodeBasedRenderer::initShaders() {
     // uses several resources from the Radium engine
     auto resourcesRootDir{ RadiumEngine::getInstance()->getResourcesDir() + "Shaders/" };
+
     m_shaderProgramManager->addShaderProgram(
         { { "Hdr2Ldr" },
           resourcesRootDir + "2DShaders/Basic2D.vert.glsl",
           resourcesRootDir + "2DShaders/Hdr2Ldr.frag.glsl" } );
+
 }
 
 void NodeBasedRenderer::initBuffers() {
     m_postprocessFbo = std::make_unique<globjects::Framebuffer>();
 
-    // Advanced renderer : only initializes textures that are shared between all the passe or
+    // NodeBased renderer : only initializes textures that are shared between all the passes or
     // that are used internally on the renderer.
     // Each pass will manage its own textures and export them if they can be shared.
 
@@ -294,7 +70,7 @@ void NodeBasedRenderer::initBuffers() {
     texparams.internalFormat = GL_DEPTH_COMPONENT24;
     texparams.format         = GL_DEPTH_COMPONENT;
     texparams.type           = GL_UNSIGNED_INT;
-    texparams.name           = "Depth ( AdvRndr )";
+    texparams.name           = "Depth (RadiumNBR)";
     m_sharedTextures.insert( { texparams.name, std::make_shared<Texture>( texparams ) } );
 
     // Linear (and HDR) RGBA Color texture
@@ -303,18 +79,43 @@ void NodeBasedRenderer::initBuffers() {
     texparams.type           = GL_FLOAT;
     texparams.minFilter      = GL_LINEAR;
     texparams.magFilter      = GL_LINEAR;
-    texparams.name           = "Linear RGB ( AdvRndr )";
+    texparams.name           = "Linear RGB (RadiumNBR)";
     m_sharedTextures.insert( { texparams.name, std::make_shared<Texture>( texparams ) } );
 }
 
+void NodeBasedRenderer::initializeInternal() {
+
+    // TODO : this must be done only once, see register system ...
+    if ( Ra::Engine::RadiumEngine::getInstance()->getSystem( "DefaultCameraManager" ) == nullptr )
+    {
+        auto cameraManager = new DefaultCameraManager();
+        Ra::Engine::RadiumEngine::getInstance()->registerSystem( "DefaultCameraManager",
+                                                                 cameraManager );
+    }
+    auto lmngr = dynamic_cast<DefaultLightManager*>(
+        Ra::Engine::RadiumEngine::getInstance()->getSystem( "DefaultLightManager" ) );
+    if ( lmngr == nullptr )
+    {
+        lmngr = new DefaultLightManager();
+        Ra::Engine::RadiumEngine::getInstance()->registerSystem( "DefaultLightManager", lmngr );
+    }
+    m_lightmanagers.push_back( lmngr );
+
+    // Initialize renderer resources
+    initShaders();
+    initBuffers();
+    for ( const auto& t : m_sharedTextures )
+    { m_secondaryTextures.insert( { t.first, t.second.get() } ); }
+}
+
 void NodeBasedRenderer::resizeInternal() {
 
-    m_sharedTextures["Depth ( AdvRndr )"]->resize( m_width, m_height );
-    m_sharedTextures["Linear RGB ( AdvRndr )"]->resize( m_width, m_height );
+    m_sharedTextures["Depth (RadiumNBR)"]->resize( m_width, m_height );
+    m_sharedTextures["Linear RGB (RadiumNBR)"]->resize( m_width, m_height );
 
     for ( auto& rp : m_renderPasses )
     {
-        rp->resize( m_width, m_height );
+        rp.second->resize( m_width, m_height );
     }
 
     m_postprocessFbo->bind();
@@ -330,54 +131,12 @@ void NodeBasedRenderer::resizeInternal() {
     globjects::Framebuffer::unbind();
 }
 
-// Todo : verify if the ro partition is the good one and that the passes use the right part.
-void NodeBasedRenderer::updateStepInternal( const ViewingParameters& renderData ) {
-    // Split objects into opaque and transparent
-    m_transparentRenderObjects.clear();
-    m_volumetricRenderObjects.clear();
-    m_allRenderObjects.clear();
-    for ( auto it = m_fancyRenderObjects.begin(); it != m_fancyRenderObjects.end(); )
-    {
-        m_allRenderObjects.push_back( *it );
-        if ( ( *it )->isTransparent() )
-        {
-            m_transparentRenderObjects.push_back( *it );
-            it = m_fancyRenderObjects.erase( it );
-        }
-        else
-        {
-            auto material = ( *it )->getMaterial();
-            if ( material &&
-                 material->getMaterialAspect() == Material::MaterialAspect::MAT_DENSITY )
-            {
-                m_volumetricRenderObjects.push_back( *it );
-                it = m_fancyRenderObjects.erase( it );
-            }
-            else
-            { ++it; }
-        }
-    }
-
-    // Update passes
-    // Note that this could be expensive. Find a way to add attributes to renderObjects so
-    // that one ca factorizes some evaluations.
-    for ( auto& rp : m_renderPasses )
-    {
-        if ( rp->isActive() ) { rp->update(); }
-    }
-}
-
 void NodeBasedRenderer::renderInternal( const ViewingParameters& renderData ) {
-    m_clearPass->setBackground( getBackgroundColor() );
-    if ( m_hasEnvMap )
-    {
-        // for ( auto &lm : m_lightmanagers ) { lm->removeSystemLights(); }
-    }
     // Run each pass, in order.
     for ( const auto& rp : m_renderPasses )
     {
-        if (rp->isActive()) {
-            rp->execute( renderData );
+        if (rp.second->isActive()) {
+            rp.second->execute( renderData );
         }
     }
 }
@@ -495,7 +254,7 @@ void NodeBasedRenderer::postProcessInternal( const ViewingParameters& /* renderD
 
     auto shader = m_shaderProgramManager->getShaderProgram( "Hdr2Ldr" );
     shader->bind();
-    shader->setUniform( "screenTexture", m_sharedTextures["Linear RGB ( AdvRndr )"].get(), 0 );
+    shader->setUniform( "screenTexture", m_sharedTextures["Linear RGB (RadiumNBR)"].get(), 0 );
     m_quadMesh->render( shader );
 
     GL_ASSERT( glDepthMask( GL_TRUE ) );
@@ -504,84 +263,30 @@ void NodeBasedRenderer::postProcessInternal( const ViewingParameters& /* renderD
     m_postprocessFbo->unbind();
 }
 
-void NodeBasedRenderer::setEnvMap( const std::string& files ) {
-    if ( files.empty() )
+// Todo : verify if the ro partition is the good one and that the passes use the right part.
+void NodeBasedRenderer::updateStepInternal( const ViewingParameters& renderData ) {
+    m_allRenderObjects.clear();
+    for ( auto it = m_fancyRenderObjects.begin(); it != m_fancyRenderObjects.end(); )
     {
-        m_clearPass->setEnvMap( nullptr );
-        m_envlightPass->setEnvMap( nullptr );
-        m_envlightPass->deactivate();
-        m_volumelightPass->setEnvMap( nullptr );
-        m_hasEnvMap = false;
+        m_allRenderObjects.push_back( *it );
     }
-    else
+
+    // Update passes
+    // Note that this could be expensive. Find a way to add attributes to renderObjects so
+    // that one ca factorizes some evaluations.
+    for ( auto& rp : m_renderPasses )
     {
-        // Todo, add type VERTICALCROSS, HORIZONTALCROSS and differentiate between all types
-        //  using file extension and file size
-        auto t = ( files.find( ';' ) != files.npos ) ? EnvMap::EnvMapType::ENVMAP_CUBE
-                                                     : EnvMap::EnvMapType::ENVMAP_PFM;
-        if ( t == EnvMap::EnvMapType::ENVMAP_PFM )
-        {
-            auto ext = files.substr( files.size() - 3 );
-            if ( ext != "pfm" ) { t = EnvMap::EnvMapType::ENVMAP_LATLON; }
-        }
-        // for now, only skyboxes are managed
-        auto e = std::make_shared<EnvMap>( files, t, true );
-        m_clearPass->setEnvMap( e );
-        m_envlightPass->setEnvMap( e );
-        // activate the envmap pass only if wireframe is inactive ?
-        if ( !m_wireframePass->isActive() ) { m_envlightPass->activate(); }
-        m_volumelightPass->setEnvMap( e );
-        m_hasEnvMap = true;
+        if ( rp.second->isActive() ) { rp.second->update(); }
     }
 }
 
-void NodeBasedRenderer::showEnvMap( bool state ) {
-    m_clearPass->showEnvMap( state );
-}
-
-void NodeBasedRenderer::setEnvStrength( int s ) {
-    m_envlightPass->setEnvStrength( s );
-}
-
-Scalar NodeBasedRenderer::getAoRadius() const {
-    if ( m_aoPass ) { return m_aoPass->aoRadius(); }
-    return 5.0;
-}
-void NodeBasedRenderer::setAoRadius( Scalar r ) {
-    if ( m_aoPass ) { m_aoPass->setAoRadius( r ); }
-}
 
-int NodeBasedRenderer::getAoSamplingDensity() const {
-    return m_aoSamplingPoints;
-}
-void NodeBasedRenderer::setAoSamplingDensity( int d ) {
-    m_aoSamplingPoints = d;
-    auto sphereSampler = std::make_unique<SphereSampler>( m_aoSamplingMethod, m_aoSamplingPoints );
-    if ( m_aoPass ) { m_aoPass->setSampler( std::move( sphereSampler ) ); }
+bool NodeBasedRenderer::addPass( std::shared_ptr<RadiumNBR::RenderPass> pass, int rank, bool defaultPass ) {
+    const auto [itPass, success] = m_renderPasses.insert( {rank, pass} );
+    if ( defaultPass && success ) {
+        m_defaultPass = rank;
+    }
+    return success;
 }
 
-void NodeBasedRenderer::wireframeMode( bool status ) {
-    enableWireframe( status );
-    if ( m_wireframe ) {
-        m_wireframePass->activate();
-
-        m_aoPass->deactivate();
-        m_emissivityPass->deactivate();
-        m_envlightPass->deactivate();
-        m_locallightPass->deactivate();
-        m_transparencyPass->deactivate();
-        m_volumelightPass->deactivate();
-
-    } else {
-        m_wireframePass->deactivate();
-
-        m_aoPass->activate();
-        m_emissivityPass->activate();
-        m_envlightPass->activate();
-        m_locallightPass->activate();
-        m_transparencyPass->activate();
-        m_volumelightPass->activate();
-
-    }
 }
-} // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/NodeBasedRenderer.hpp b/src/libRender/RadiumNBR/NodeBasedRenderer.hpp
index 49c4c3a..53d1ad7 100644
--- a/src/libRender/RadiumNBR/NodeBasedRenderer.hpp
+++ b/src/libRender/RadiumNBR/NodeBasedRenderer.hpp
@@ -1,7 +1,6 @@
 #pragma once
 #include <RadiumNBR/NodeBasedRendererMacro.hpp>
 #include <RadiumNBR/RenderPass.hpp>
-#include <RadiumNBR/SphereSampler.hpp>
 
 #include <Engine/Rendering/Renderer.hpp>
 
@@ -9,26 +8,19 @@ namespace globjects {
 class Framebuffer;
 }
 
-namespace Ra::Engine::Data {
-class Texture;
-} // namespace Ra::Engine::Data
-
 namespace RadiumNBR {
-class ClearPass;
-class GeomPrePass;
-class AccessibilityBufferPass;
-class EmissivityPass;
-class EnvLightPass;
-class LocalLightPass;
-class VolumeLightingPass;
-class TransparencyPass;
-class WireframePass;
-
-/** Advanced renderer for the Radium Engine
- * This class implements a forward rendering algorithm with Z-prepass, multipass light accumulation
- * for opaque and transparent objects.
- * Once renderer, the final is composited with Ui, debug and
- * X-ray objects renderings on demand.
+
+/** Node based for the Radium Engine
+ * This Renderer is fully configurable, either dynammically or programatically.
+ * It implements the Ra::Engine::Rendering/Renderer interface and cand be configured by adding
+ * as many RadiumNBR::RenderPass rendering passes.
+ *
+ * It defines two textures that migh be shared between passes :
+ *  - a depth buffer attachable texture
+ *  - a Linear space RGBA color texture
+ *
+ *  It allows to apply a linearRGB to sRGB color transformation before displaying the image.
+ *
  *
  * @see rendering.md for description of the renderer
  */
@@ -39,19 +31,23 @@ class NodeBasedRenderer_LIBRARY_API NodeBasedRenderer : public Ra::Engine::Rende
     NodeBasedRenderer();
     ~NodeBasedRenderer() override;
 
-    [[nodiscard]] std::string getRendererName() const override { return "Node Based Renderer"; }
+    [[nodiscard]] std::string getRendererName() const override { return "Configurable Renderer"; }
+
     bool buildRenderTechnique( Ra::Engine::Rendering::RenderObject* ro ) const override;
 
-    void setEnvMap( const std::string& files );
-    void showEnvMap( bool state );
-    // TODO : define a way to configfure each passes without adding here specifi methods
-    // TODO the following might be removed in the future
-    Scalar getAoRadius() const;
-    void setAoRadius( Scalar r );
-    int getAoSamplingDensity() const;
-    void setAoSamplingDensity( int d );
-    void setEnvStrength( int s );
-    void wireframeMode( bool status );
+    /** add a pass to be executed at the given rank, and, optionally, is the default pass for the
+     * renderer.
+     *
+     * Passes are executed by increasing rank.
+     * [not yet available : If two passes have the same rank, they will be
+     * executed in arbitrary order].
+     *
+     * The default pass (rank 0) might be used for specific render operations.
+     *
+     * If a pass with the same rank already exists in the Renderer, the given pass is not added nor is changed the default)
+     * @return true if the pass is insserted, false if not
+    */
+    bool addPass( std::shared_ptr<RadiumNBR::RenderPass> pass, int rank, bool defaultPass = false );
 
   protected:
     void initializeInternal() override;
@@ -62,39 +58,34 @@ class NodeBasedRenderer_LIBRARY_API NodeBasedRenderer : public Ra::Engine::Rende
     void debugInternal( const Ra::Engine::Data::ViewingParameters& renderData ) override;
     void uiInternal( const Ra::Engine::Data::ViewingParameters& renderData ) override;
 
+    virtual void initShaders();
+    virtual void initBuffers();
+
+    inline std::map<std::string, std::shared_ptr<Ra::Engine::Data::Texture>> &sharedTextures() {
+        return m_sharedTextures;
+    }
+
+    inline globjects::Framebuffer *postprocessFbo() {
+        return m_postprocessFbo.get();
+    }
+
+    inline std::map< int, std::shared_ptr<RenderPass> >& renderPasses() {
+        return m_renderPasses;
+    }
+
+    inline const std::map< int, std::shared_ptr<RenderPass> >& renderPasses() const {
+        return m_renderPasses;
+    }
+
+    inline std::vector<RenderObjectPtr> *allRenderObjects() {
+        return &m_allRenderObjects;
+    }
+
+    inline int defaultPass() const {
+        return m_defaultPass;
+    }
+
   private:
-    void initPasses();
-    void initShaders();
-    void initBuffers();
-
-#if 0
-    struct NodeBasedRendererPasses {
-        static constexpr Ra::Core::Utils::Index DEFAULT_PASS{0};
-        static constexpr Ra::Core::Utils::Index LIGHTING_OPAQUE_PASS{DEFAULT_PASS};
-        static constexpr Ra::Core::Utils::Index GEOM_PASS{1};
-        static constexpr Ra::Core::Utils::Index ACCESSIBILITY_PASS{2};
-        static constexpr Ra::Core::Utils::Index CLEAR_PASS{3};
-        static constexpr Ra::Core::Utils::Index ENVMAP_LIGHTING_OPAQUE_PASS{4};
-        static constexpr Ra::Core::Utils::Index EMISSIVITY_PASS{5};
-        static constexpr Ra::Core::Utils::Index LIGHTING_TRANSPARENT_PASS{6};
-        static constexpr Ra::Core::Utils::Index LIGHTING_VOLUME_PASS{7};
-    };
-#else
-    enum NodeBasedRendererPasses : int {
-        DEFAULT_PASS         = 0,
-        LIGHTING_OPAQUE_PASS = DEFAULT_PASS,
-        GEOM_PASS,
-        ACCESSIBILITY_PASS,
-        CLEAR_PASS,
-        ENVMAP_LIGHTING_OPAQUE_PASS,
-        EMISSIVITY_PASS,
-        LIGHTING_TRANSPARENT_PASS,
-        LIGHTING_VOLUME_PASS,
-        WIREFRAME_PASS,
-        NUM_PASSES
-    };
-#endif
-  protected:
     /// textures own by the Renderer but shared across passes
     std::map<std::string, std::shared_ptr<Ra::Engine::Data::Texture>> m_sharedTextures;
 
@@ -102,51 +93,13 @@ class NodeBasedRenderer_LIBRARY_API NodeBasedRenderer : public Ra::Engine::Rende
     std::unique_ptr<globjects::Framebuffer> m_postprocessFbo;
 
     /// vector of this renderer render passes, sorted by precedence (first pass is at index 0)
-    std::vector<std::shared_ptr<RenderPass>> m_renderPasses;
-
-    /// Subset of the objects that are transparent and need special rendering
-    std::vector<RenderObjectPtr> m_transparentRenderObjects;
-
-    /// Subset of the objects that are volumetric
-    std::vector<RenderObjectPtr> m_volumetricRenderObjects;
+    std::map< int, std::shared_ptr<RenderPass> > m_renderPasses;
 
     /// Backup of all objects so that global passses can execute
     std::vector<RenderObjectPtr> m_allRenderObjects;
 
-    /// The ambiant occlusion pass
-    std::shared_ptr<AccessibilityBufferPass> m_aoPass;
-    // The sampling method of the sphere sampler
-    SphereSampler::SamplingMethod m_aoSamplingMethod{ SphereSampler::SamplingMethod::HAMMERSLEY };
-    // The number of points for the sampler
-    int m_aoSamplingPoints{ 64 };
-
-    /// clear the final ouput image
-    std::shared_ptr<ClearPass> m_clearPass;
-
-    /// The zprepass
-    std::shared_ptr<GeomPrePass> m_zPrePass;
-
-    /// The emissivity  pass
-    std::shared_ptr<EmissivityPass> m_emissivityPass;
-
-    /// The envlight  pass
-    std::shared_ptr<EnvLightPass> m_envlightPass;
-
-    /// The local lighting pass
-    std::shared_ptr<LocalLightPass> m_locallightPass;
-
-    /// The volume lighting pass
-    std::shared_ptr<VolumeLightingPass> m_volumelightPass;
-
-    /// The transparency (Order independant transparency) pass
-    std::shared_ptr<TransparencyPass> m_transparencyPass;
-
-    /// The transparency (Order independant transparency) pass
-    std::shared_ptr<WireframePass> m_wireframePass;
-
-    /// Is an envmap attached to the renderer
-    bool m_hasEnvMap{ false };
-
+    /// The default pass
+    int m_defaultPass{-1};
 };
 
-} // namespace RadiumNBR
+}
diff --git a/src/libRender/RadiumNBR/RenderPass.hpp b/src/libRender/RadiumNBR/RenderPass.hpp
index e9b7156..8e07a08 100644
--- a/src/libRender/RadiumNBR/RenderPass.hpp
+++ b/src/libRender/RadiumNBR/RenderPass.hpp
@@ -124,6 +124,8 @@ class NodeBasedRenderer_LIBRARY_API RenderPass
     // is the pass active ?
     [[nodiscard]] inline bool isActive() { return m_active; }
 
+    [[nodiscard]] inline int index() const { return int(m_idx); }
+
     /// @todo : Having protected member is a bad idea
     /// @see https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c133-avoid-protected-data
   protected:
-- 
GitLab