From 39bade978999a5443fe1ccf1bfdc250f3be834b3 Mon Sep 17 00:00:00 2001
From: Mathias Paulin <mathias.paulin@irit.fr>
Date: Wed, 7 Apr 2021 19:02:04 +0200
Subject: [PATCH] Add envmap to the visualization renderer.

---
 src/DemoApp/main.cpp                          |  19 ++-
 .../RadiumNBR/Gui/VisualizationGui.cpp        |  28 ++++
 .../Passes/CustomAttribToColorPass.cpp        | 150 ++++++++++++------
 .../Passes/CustomAttribToColorPass.hpp        |  43 ++++-
 .../Renderer/FullFeatureRenderer.hpp          |   4 +-
 .../RadiumNBR/Renderer/Visualization.cpp      |  34 ++++
 .../RadiumNBR/Renderer/Visualization.hpp      |  26 ++-
 7 files changed, 241 insertions(+), 63 deletions(-)

diff --git a/src/DemoApp/main.cpp b/src/DemoApp/main.cpp
index f299b13..a2b6dd5 100644
--- a/src/DemoApp/main.cpp
+++ b/src/DemoApp/main.cpp
@@ -166,16 +166,23 @@ const std::string customGeomAttrib{"in vec4 geomCustomAttrib[];\n"
  * add an edition widget as in Radium app demo
  */
 const std::string customFragmentColor{
-    "in vec4 fragCustomAttrib;\n"
-    "\nvec4 computeCustomColor(Material mat, vec3 lightDir, vec3 viewDir, vec3 normal_world) {\n"
+    "in vec4 fragCustomAttrib;"
+    "\nvec4 computeCustomColor(Material mat, vec3 lightDir, vec3 viewDir, vec3 normal_world) { \n"
     "vec3 diffColor; \n"
     "vec3 specColor; \n"
     "getSeparateBSDFComponent( mat, getPerVertexTexCoord(), lightDir,  viewDir,\n"
     "vec3(0, 0, 1), diffColor, specColor );\n"
-    "vec3 col = normal_world*0.5+0.5;\n"
-    "col *= fragCustomAttrib.rgb;\n"
-    "return vec4( (specColor+col)*max(lightDir.z, 0), 1); \n"
-    "}\n"};
+    "vec3 envd;\n"
+    "vec3 envs;\n"
+    "int e = getEnvMapColors(mat, normal_world, envd, envs);"
+    "vec3 finalColor = normal_world*0.5+0.5;\n"
+    "finalColor *= fragCustomAttrib.rgb;\n"
+    "if (e==1) { finalColor = finalColor*envd + specColor*envs; }\n"
+    "else { finalColor = (finalColor + specColor) * max(lightDir.z, 0) \n"
+    "       * lightContributionFrom(light, getWorldSpacePosition().xyz); }\n"
+    "return vec4( finalColor, 1); \n"
+    "}\n"
+   };
 
 /**
  *      Main code to demonstrate the use of a self configured node based renderer
diff --git a/src/libRender/RadiumNBR/Gui/VisualizationGui.cpp b/src/libRender/RadiumNBR/Gui/VisualizationGui.cpp
index c8b7869..72d9640 100644
--- a/src/libRender/RadiumNBR/Gui/VisualizationGui.cpp
+++ b/src/libRender/RadiumNBR/Gui/VisualizationGui.cpp
@@ -50,6 +50,34 @@ buildControllerGui( NodeBasedRenderer* renderer, const std::function<void()>& ap
         false );
     controlPanel->endLayout( true );
 
+    controlPanel->beginLayout( QBoxLayout::LeftToRight );
+    auto envmpClbck = [&controller, appUpdateCallback]( const std::string& files ) {
+      controller.setEnvMap( files );
+      appUpdateCallback();
+    };
+    controlPanel->addFileInput(
+        "Environment map", envmpClbck, "../", "Images (*.png *.jpg *.pfm *.exr *hdr)" );
+
+    controlPanel->beginLayout( QBoxLayout::TopToBottom );
+    controlPanel->addOption(
+        " Show envMap ",
+        [&controller, appUpdateCallback]( bool b ) {
+          controller.showEnvMap( b );
+          appUpdateCallback();
+        },
+        true );
+
+    controlPanel->addSliderInput(
+        " Environment strength ",
+        [&controller, appUpdateCallback]( int v ) {
+          controller.setEnvStrength( v );
+          appUpdateCallback();
+        },
+        100 );
+    controlPanel->endLayout();
+    controlPanel->endLayout( true );
+
+
     // TODO : add a double slider to get the good value. ask @dlyr for its doubleSliderClass ...
     controlPanel->beginLayout( QBoxLayout::LeftToRight );
     auto splatSizeCtrl = [&controller]( double v ) { controller.setSplatSize( v ); };
diff --git a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp
index 2ebbe4e..fd42a97 100644
--- a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp
+++ b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp
@@ -19,6 +19,35 @@ using namespace Ra::Core::Utils; // log
 namespace RadiumNBR {
 using namespace gl;
 
+// Envmap shader source
+static const std::string noopEnvMapFunction{
+    "\nint getEnvMapColors(Material mat, vec3 normalWorld, out vec3 diffuse, out vec3 specular) {\n"
+    "   diffuse = vec3(1); specular = vec3(1); return 0;\n"
+    "}\n\n"
+};
+
+static const std::string envMapFunction{
+    "\nuniform samplerCube envTexture;\n"
+    "uniform int numLod;\n"
+    "uniform mat4 redShCoeffs;\n"
+    "uniform mat4 greenShCoeffs;\n"
+    "uniform mat4 blueShCoeffs;\n"
+    "uniform float envStrength;\n"
+    "const float OneOverPi = 0.3183098862;\n"
+    "int getEnvMapColors(Material mat, vec3 normalWorld, out vec3 diffuse, out vec3 specular) {\n"
+    "   vec3 view = normalize(in_viewVector);\n"
+    "   vec3 rfl = reflect(-view, normalWorld.xyz);\n"
+    "   vec4 up = vec4(normalWorld, 1);\n"
+    "   diffuse.r = dot(up, redShCoeffs * up);\n"
+    "   diffuse.g = dot(up, greenShCoeffs * up);\n"
+    "   diffuse.b = dot(up, blueShCoeffs * up);\n"
+    "   diffuse *= envStrength/OneOverPi;\n"
+    "   float cosTi = clamp(dot(rfl, normalWorld), 0.001, 1.);\n"
+    "   float r = getGGXRoughness(material, getPerVertexTexCoord()) * numLod;\n"
+    "   specular = textureLod(envTexture, rfl, r).rgb  * cosTi * envStrength;\n"
+    "   return 1;\n"
+    "}\n\n"};
+
 /* Test Point cloud parameter provider */
 /*
  * WARNING : this class is here only for testing and experimentation purpose.
@@ -114,16 +143,23 @@ void CustomAttribToColorPass::execute(
 
     glClearBufferfv( GL_DEPTH, 0, &clearDepth );
 
+
+    if ( m_envmap ) {
+        m_passParams.addParameter( "envStrength", m_envmap->getEnvStrength() );
+    }
+
+    // TODO : if an envmap is set, render with the envmap only ?
     if ( m_lightmanager->count() > 0 )
     {
         // for ( const auto& l : m_lights )
         for ( size_t i = 0; i < m_lightmanager->count(); ++i )
         {
             Ra::Engine::Data::RenderParameters passParams;
-            passParams.concatParameters( m_passParams );
             const auto l = m_lightmanager->getLight( i );
             l->getRenderParameters( passParams );
-
+            if (m_envmap) {
+                passParams.concatParameters( m_passParams );
+            }
             for ( const auto& ro : *m_objectsToRender )
             {
                 ro->render( passParams, viewParams, passIndex() );
@@ -150,7 +186,7 @@ bool CustomAttribToColorPass::buildRenderTechnique(
              {"CustomAtt2ClrPass::CustomColorProgram" + ro->getName()} ) )
     {
         rt.setConfiguration( *cfg, passIndex() );
-        // TODO : warning, the param provider might be erroneous
+        // TODO : is this really usefull ?
         rt.setParametersProvider( mat, passIndex() );
     }
     else
@@ -238,8 +274,14 @@ bool CustomAttribToColorPass::buildRenderTechnique(
                                                         ro->getName()};
         theConfig.addShaderSource( Ra::Engine::Data::ShaderType::ShaderType_VERTEX,
                                    vertexShaderSource + m_customVertexAttrib );
-        theConfig.addShaderSource( Ra::Engine::Data::ShaderType::ShaderType_FRAGMENT,
-                                   fragmentShadersource + m_customFragmentColor );
+        if ( m_envmap ) {
+            theConfig.addShaderSource( Ra::Engine::Data::ShaderType::ShaderType_FRAGMENT,
+                                       fragmentShadersource + envMapFunction + m_customFragmentColor );
+        } else {
+            theConfig.addShaderSource( Ra::Engine::Data::ShaderType::ShaderType_FRAGMENT,
+                                       fragmentShadersource + noopEnvMapFunction + m_customFragmentColor );
+        }
+
         theConfig.addInclude( "\"" + mat->getMaterialName() + ".glsl\"",
                               Ra::Engine::Data::ShaderType::ShaderType_FRAGMENT );
 
@@ -285,39 +327,39 @@ bool CustomAttribToColorPass::buildRenderTechnique(
             "layout( location = 6 ) out vec3 out_lightVector;\n"
             "void propagateAttributes();\n"
             "void main() {\n"
-                "vec3 eye = -transform.view[3].xyz * mat3( transform.view );\n"
-                "// if no normal is provided, splats are aligned with the screen plane\n"
-                "vec3 normal = in_normal[0];\n"
-                "if ( length( normal ) < 0.1 ) { normal = normalize( eye - in_position[0] ); }\n"
-                "// orthonormal basis {u, v, normal}\n"
-                "vec3 u = vec3( 1, 0, 0 );\n"
-                "if ( abs( normal.x ) > 1e-3 ) { u = normalize( vec3( -normal.z / normal.x, 0, 1 ) ); }\n"
-                "vec3 v = normalize( cross( normal, u ) );\n"
-                "vec3 point[4];\n"
-                "point[0] = in_position[0] - pointCloudSplatRadius * ( u + v );\n"
-                "point[1] = point[0] + pointCloudSplatRadius * u * 2;\n"
-                "point[2] = point[0] + pointCloudSplatRadius * v * 2;\n"
-                "point[3] = point[0] + pointCloudSplatRadius * ( u + v ) * 2;\n"
-                "vec2 uv[4];\n"
-                "uv[0] = vec2( -1, -1 );\n"
-                "uv[1] = vec2( -1, +1 );\n"
-                "uv[2] = vec2( +1, -1 );\n"
-                "uv[3] = vec2( +1, +1 );\n"
-                "for ( int idx = 0; idx < 4; ++idx ) {\n"
-                "    gl_Position     = transform.proj * transform.view * vec4( point[idx], 1 );\n"
-                "    out_position    = point[idx];\n"
-                "    out_texcoord    = vec3( uv[idx], 0. );\n"
-                "    out_normal      = normal;\n"
-                "    out_tangent     = in_tangent[0];\n"
-                "    out_viewVector  = in_viewVector[0];\n"
-                "    out_lightVector = in_lightVector[0];\n"
-                "    out_vertexcolor = in_vertexColor[0];\n"
-                "    propagateAttributes();"
-                "    EmitVertex();\n"
-                "}\n"
-               "EndPrimitive();\n"
+            "vec3 eye = -transform.view[3].xyz * mat3( transform.view );\n"
+            "// if no normal is provided, splats are aligned with the screen plane\n"
+            "vec3 normal = in_normal[0];\n"
+            "if ( length( normal ) < 0.1 ) { normal = normalize( eye - in_position[0] ); }\n"
+            "// orthonormal basis {u, v, normal}\n"
+            "vec3 u = vec3( 1, 0, 0 );\n"
+            "if ( abs( normal.x ) > 1e-3 ) { u = normalize( vec3( -normal.z / normal.x, 0, 1 ) ); "
             "}\n"
-        };
+            "vec3 v = normalize( cross( normal, u ) );\n"
+            "vec3 point[4];\n"
+            "point[0] = in_position[0] - pointCloudSplatRadius * ( u + v );\n"
+            "point[1] = point[0] + pointCloudSplatRadius * u * 2;\n"
+            "point[2] = point[0] + pointCloudSplatRadius * v * 2;\n"
+            "point[3] = point[0] + pointCloudSplatRadius * ( u + v ) * 2;\n"
+            "vec2 uv[4];\n"
+            "uv[0] = vec2( -1, -1 );\n"
+            "uv[1] = vec2( -1, +1 );\n"
+            "uv[2] = vec2( +1, -1 );\n"
+            "uv[3] = vec2( +1, +1 );\n"
+            "for ( int idx = 0; idx < 4; ++idx ) {\n"
+            "    gl_Position     = transform.proj * transform.view * vec4( point[idx], 1 );\n"
+            "    out_position    = point[idx];\n"
+            "    out_texcoord    = vec3( uv[idx], 0. );\n"
+            "    out_normal      = normal;\n"
+            "    out_tangent     = in_tangent[0];\n"
+            "    out_viewVector  = in_viewVector[0];\n"
+            "    out_lightVector = in_lightVector[0];\n"
+            "    out_vertexcolor = in_vertexColor[0];\n"
+            "    propagateAttributes();"
+            "    EmitVertex();\n"
+            "}\n"
+            "EndPrimitive();\n"
+            "}\n"};
 
         auto pointCloudConst =
             dynamic_cast<const Ra::Engine::Scene::PointCloudComponent*>( ro->getComponent() );
@@ -328,7 +370,7 @@ bool CustomAttribToColorPass::buildRenderTechnique(
                                        geometryShadersource + m_customGeometryAttrib );
             // construct the parameter provider for the technique
             paramProvider = std::make_shared<PointCloudParameterProvider>( mat, pointCloud );
-            pointCloud->setSplatSize(0.025);
+            pointCloud->setSplatSize( 0.025 );
         }
         // Add to the ShaderConfigManager
         Ra::Engine::Data::ShaderConfigurationFactory::addConfiguration( theConfig );
@@ -343,8 +385,8 @@ bool CustomAttribToColorPass::buildRenderTechnique(
 void CustomAttribToColorPass::setAttribToColorFunc( const std::string& vertex_source,
                                                     const std::string& geometry_source,
                                                     const std::string& fragment_source ) {
-    m_customVertexAttrib  = vertex_source;
-    m_customFragmentColor = fragment_source;
+    m_customVertexAttrib   = vertex_source;
+    m_customFragmentColor  = fragment_source;
     m_customGeometryAttrib = geometry_source;
     rebuildShaders();
 }
@@ -363,14 +405,32 @@ void CustomAttribToColorPass::rebuildShaders() {
     m_needConfigRebuild = false;
 }
 
-void CustomAttribToColorPass::setSplatSize(float s) {
+void CustomAttribToColorPass::setSplatSize( float s ) {
     m_splatsSize = s;
-    for ( auto ro : *m_objectsToRender ) {
+    for ( auto ro : *m_objectsToRender )
+    {
         auto pointCloud =
             dynamic_cast<Ra::Engine::Scene::PointCloudComponent*>( ro->getComponent() );
-        if ( pointCloud ) {
-            pointCloud->setSplatSize( m_splatsSize );
-        }
+        if ( pointCloud ) { pointCloud->setSplatSize( m_splatsSize ); }
+    }
+}
+
+void CustomAttribToColorPass::setEnvMap( std::shared_ptr<EnvMap> envmp ) {
+    m_envmap = envmp;
+    if ( m_envmap )
+    {
+        // Set the envmap parameter once.
+        // Warning : there is no way to remove parameters. Might segfault if this is not consistent
+        // with rendering usage
+        m_passParams.addParameter( "redShCoeffs", m_envmap->getShMatrix( 0 ) );
+        m_passParams.addParameter( "greenShCoeffs", m_envmap->getShMatrix( 1 ) );
+        m_passParams.addParameter( "blueShCoeffs", m_envmap->getShMatrix( 2 ) );
+        m_passParams.addParameter( "envTexture", m_envmap->getEnvTexture() );
+        int numLod = std::log2(
+            std::min( m_envmap->getEnvTexture()->width(), m_envmap->getEnvTexture()->height() ) );
+        m_passParams.addParameter( "numLod", numLod );
     }
+    rebuildShaders();
 }
+
 } // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp
index 343e952..7e1309a 100644
--- a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp
+++ b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp
@@ -1,5 +1,6 @@
 #pragma once
 #include <RadiumNBR/RenderPass.hpp>
+#include <RadiumNBR/EnvMap.hpp>
 
 #include <Core/Utils/Color.hpp>
 
@@ -53,9 +54,24 @@ class CustomAttribToColorPass : public RenderPass
     /// Set the splat size for pointclouds
     inline float getSplatSize() { return m_splatsSize; }
 
-    // return the splatSize used for point clouds
+    /// return the splatSize used for point clouds
     void setSplatSize(float s);
 
+    /// set the env map to use
+    void setEnvMap( std::shared_ptr<EnvMap> envmp );
+
+    /// set the strength (aka "power") of the env map
+    void setEnvStrength( int s ) {
+        if ( m_envmap )
+        {
+            m_envStrength = float( s ) / 100.f;
+            m_envmap->setEnvStrength( m_envStrength );
+        }
+    }
+
+    /// get the strength (aka "power") of the env map
+    int getEnvStrength() const { return int( m_envStrength * 100 ); }
+
   private:
     /// The framebuffer used to render this pass
     std::unique_ptr<globjects::Framebuffer> m_fbo{nullptr};
@@ -74,12 +90,21 @@ class CustomAttribToColorPass : public RenderPass
     std::string m_customGeometryAttrib{"void propagateAttributes(){}\n"};
 
     /// the custom FragmentColor shader function
-    /// If not changed, compute the same expression (without ao) than the light stage
+    /// If not changed, compute the same expression (without ao) than the envlight stage
     std::string m_customFragmentColor{
-        "vec4 computeCustomColor(Material mat, vec3 light_dir, vec3 view_dir) {\n"
-        "vec3 bsdf= evaluateBSDF(material, getPerVertexTexCoord(), light_dir, view_dir);\n"
-        "vec3 contribution    = lightContributionFrom(light, getWorldSpacePosition().xyz);\n"
-        "return vec4(bsdf * 3.141592 * contribution, 1.0);\n"
+        "\nvec4 computeCustomColor(Material mat, vec3 lightDir, vec3 viewDir, vec3 normal_world) { \n"
+        "vec3 diffColor; \n"
+        "vec3 specColor; \n"
+        "getSeparateBSDFComponent( mat, getPerVertexTexCoord(), lightDir,  viewDir,\n"
+        "vec3(0, 0, 1), diffColor, specColor );\n"
+        "vec3 envd;\n"
+        "vec3 envs;\n"
+        "int e = getEnvMapColors(mat, normal_world, envd, envs);"
+        "vec3 finalColor;\n"
+        "if (e==1) { finalColor = diffColor*envd + specColor*envs; }\n"
+        "else { finalColor = (diffColor + specColor) * max(lightDir.z, 0) \n"
+        "       * lightContributionFrom(light, getWorldSpacePosition().xyz); }\n"
+        "return vec4( finalColor, 1); \n"
         "}\n"};
 
     /// State indicator
@@ -90,5 +115,11 @@ class CustomAttribToColorPass : public RenderPass
 
     /// The splats size
     float m_splatsSize{0.025};
+
+    /// The Environment to used for envmap lighting
+    std::shared_ptr<EnvMap> m_envmap{nullptr};
+
+    /// The strength of the envmap
+    float m_envStrength;
 };
 } // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/Renderer/FullFeatureRenderer.hpp b/src/libRender/RadiumNBR/Renderer/FullFeatureRenderer.hpp
index e49e073..256181a 100644
--- a/src/libRender/RadiumNBR/Renderer/FullFeatureRenderer.hpp
+++ b/src/libRender/RadiumNBR/Renderer/FullFeatureRenderer.hpp
@@ -37,13 +37,15 @@ class NodeBasedRenderer_LIBRARY_API FullFeatureRenderer final : public NodeBased
 
     void setEnvMap( const std::string& files );
     void showEnvMap( bool state );
+    void setEnvStrength( int s );
+
     // TODO : define a way to configure 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:
diff --git a/src/libRender/RadiumNBR/Renderer/Visualization.cpp b/src/libRender/RadiumNBR/Renderer/Visualization.cpp
index df101c6..d1cfb76 100644
--- a/src/libRender/RadiumNBR/Renderer/Visualization.cpp
+++ b/src/libRender/RadiumNBR/Renderer/Visualization.cpp
@@ -118,4 +118,38 @@ void VisualizationController::setSplatSize( float s ) {
     m_customPass->setSplatSize( s );
 };
 
+
+void VisualizationController::setEnvMap( const std::string& files ) {
+    if ( files.empty() )
+    {
+        m_clearPass->setEnvMap( nullptr );
+        m_customPass->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_customPass->setEnvMap( e );
+        m_hasEnvMap = true;
+    }
+}
+
+void VisualizationController::showEnvMap( bool state ) {
+    m_clearPass->showEnvMap( state );
+}
+
+void VisualizationController::setEnvStrength( int s ) {
+    m_customPass->setEnvStrength( s );
+}
 } // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/Renderer/Visualization.hpp b/src/libRender/RadiumNBR/Renderer/Visualization.hpp
index bf1973c..5c48202 100644
--- a/src/libRender/RadiumNBR/Renderer/Visualization.hpp
+++ b/src/libRender/RadiumNBR/Renderer/Visualization.hpp
@@ -38,25 +38,41 @@ class NodeBasedRenderer_LIBRARY_API VisualizationController
     float getSplatSize();
     void setSplatSize( float s );
 
+    void setEnvMap( const std::string& files );
+    void showEnvMap( bool state );
+    void setEnvStrength( int s );
+
   private:
     /// The custom pass if needed for modification
     std::shared_ptr<RadiumNBR::CustomAttribToColorPass> m_customPass;
 
     /// The clear pass for the background
     std::shared_ptr<RadiumNBR::ClearPass> m_clearPass;
-    ///
+    /// TODO : this default are the same as in the CustomAttribToColorPass. Use them instead of redefining here
     std::string m_vertexFunction{"void outputCustomAttribs() {\n}\n"};
     std::string m_geometryFunction{"void propagateAttributes(){}\n"};
     std::string m_fragmentFunction{
-        "vec4 computeCustomColor(Material mat, vec3 light_dir, vec3 view_dir) {\n"
-        "vec3 bsdf= evaluateBSDF(material, getPerVertexTexCoord(), light_dir, view_dir);\n"
-        "vec3 contribution    = lightContributionFrom(light, getWorldSpacePosition().xyz);\n"
-        "return vec4(bsdf * 3.141592 * contribution, 1.0);\n"
+        "\nvec4 computeCustomColor(Material mat, vec3 lightDir, vec3 viewDir, vec3 normal_world) { \n"
+        "vec3 diffColor; \n"
+        "vec3 specColor; \n"
+        "getSeparateBSDFComponent( mat, getPerVertexTexCoord(), lightDir,  viewDir,\n"
+        "vec3(0, 0, 1), diffColor, specColor );\n"
+        "vec3 envd;\n"
+        "vec3 envs;\n"
+        "int e = getEnvMapColors(mat, normal_world, envd, envs);"
+        "vec3 finalColor;\n"
+        "if (e==1) { finalColor = diffColor*envd + specColor*envs; }\n"
+        "else { finalColor = (diffColor + specColor) * max(lightDir.z, 0) \n"
+        "       * lightContributionFrom(light, getWorldSpacePosition().xyz); }\n"
+        "return vec4( finalColor, 1); \n"
         "}\n"};
 
     RadiumNBR::NodeBasedRenderer* m_renderer;
 
     bool m_postProcess{true};
+
+    /// Is an envmap attached to the renderer
+    bool m_hasEnvMap{false};
 };
 
 } // namespace RadiumNBR
-- 
GitLab