From fbc1df781256085f7a705f7cfa26239292edb27e Mon Sep 17 00:00:00 2001 From: Mathias Paulin <mathias.paulin@irit.fr> Date: Wed, 24 Mar 2021 18:57:27 +0100 Subject: [PATCH] Add a lighting pass with customization of final color computation. --- src/DemoApp/main.cpp | 302 ++++++++++++++++-- src/libRender/CMakeLists.txt | 2 + src/libRender/RadiumNBR/NodeBasedRenderer.hpp | 3 + .../Passes/CustomAttribToColorPass.cpp | 247 ++++++++++++++ .../Passes/CustomAttribToColorPass.hpp | 66 ++++ 5 files changed, 590 insertions(+), 30 deletions(-) create mode 100644 src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp create mode 100644 src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp diff --git a/src/DemoApp/main.cpp b/src/DemoApp/main.cpp index 3326b89..8082e91 100644 --- a/src/DemoApp/main.cpp +++ b/src/DemoApp/main.cpp @@ -2,50 +2,73 @@ #include <Gui/BaseApplication.hpp> #include <Gui/RadiumWindow/SimpleWindowFactory.hpp> +// To manage the scene +#include <Core/Geometry/MeshPrimitives.hpp> +#include <Engine/Scene/EntityManager.hpp> +#include <Engine/Scene/GeometryComponent.hpp> +#include <Engine/Scene/GeometrySystem.hpp> + // Include the customizable renderer system (only the used part here) #include <RadiumNBR/NodeBasedRenderer.hpp> #include <RadiumNBR/Passes/ClearPass.hpp> +#include <RadiumNBR/Passes/CustomAttribToColorPass.hpp> #include <RadiumNBR/Passes/GeomPrepass.hpp> -#include <RadiumNBR/Passes/WireframePass.hpp> +// To add attribs to loaded objects +#include <Engine/Data/Mesh.hpp> +#include <Engine/Rendering/RenderObject.hpp> +#include <Engine/Rendering/RenderObjectManager.hpp> +#include <Engine/Scene/EntityManager.hpp> +#include <Engine/Scene/SystemDisplay.hpp> + +#include <Core/Utils/Log.hpp> +using namespace Ra::Core::Utils; // log /** * This class parameterize the renderer just after the OpenGL system was initialized. - * In the functor, The OpenGL context of the drawing window is activated. + * when a method of this controler is called, the OpenGL context of the drawing window is activated. */ -class RendererController : public RadiumNBR::RenderControlFunctor { +class RendererController : public RadiumNBR::RenderControlFunctor +{ public: - - void configure(RadiumNBR::NodeBasedRenderer *renderer, int w, int h) override { - LOG( Ra::Core::Utils::logINFO ) << "Customizing the renderer " << renderer->getRendererName(); + /* + * Called once : configure the renderer by adding passes and allocating controler resources + */ + void configure( RadiumNBR::NodeBasedRenderer* renderer, int w, int h ) override { + LOG( Ra::Core::Utils::logINFO ) + << "Customizing the renderer " << renderer->getRendererName(); //! [Caching some helpers and data from the Engine and the renderer] auto resourcesCheck = Ra::Core::Resources::getResourcesPath( - reinterpret_cast<void*>( &RadiumNBR::NodeBasedRendererMagic ), { "Resources/RadiumNBR" } ); + reinterpret_cast<void*>( &RadiumNBR::NodeBasedRendererMagic ), + {"Resources/RadiumNBR"} ); if ( !resourcesCheck ) { LOG( Ra::Core::Utils::logERROR ) << "Unable to find resources for NodeBasedRenderer!"; return; - } else { - LOG( Ra::Core::Utils::logINFO ) << "NodeBasedRenderer Resources are at " << *resourcesCheck; } - auto resourcesPath{ *resourcesCheck }; + else + { + LOG( Ra::Core::Utils::logINFO ) + << "NodeBasedRenderer Resources are at " << *resourcesCheck; + } + auto resourcesPath{*resourcesCheck}; auto shaderManager = Ra::Engine::RadiumEngine::getInstance()->getShaderProgramManager(); - auto colortexture = renderer->sharedTextures().find( "Linear RGB (RadiumNBR)"); - auto depthtexture = renderer->sharedTextures().find( "Depth (RadiumNBR)" ); + auto colortexture = renderer->sharedTextures().find( "Linear RGB (RadiumNBR)" ); + auto depthtexture = renderer->sharedTextures().find( "Depth (RadiumNBR)" ); //! [Caching some helpers and data from the Engine and the renderer] //! [Adding a clear-screen pass] { - // pass that draw no object and is positionned at rank 0 + // pass that draw no object and is positioned at rank 0 auto pass = std::make_shared<RadiumNBR::ClearPass>( nullptr, 0 ); // set the output of the pass : clear the renderer Linear RGB output texture pass->setOutput( *colortexture ); // set the clear color (pass internal state) - pass->setBackground( renderer->getBackgroundColor()); + pass->setBackground( renderer->getBackgroundColor() ); pass->initializePass( w, h, shaderManager ); // add the pass to the renderer and activate it - renderer->addPass(pass, pass->index()); + renderer->addPass( pass, pass->index() ); pass->activate(); } //! [Adding a clear-screen pass] @@ -60,28 +83,65 @@ class RendererController : public RadiumNBR::RenderControlFunctor { pass->setResourcesDir( resourcesPath ); pass->initializePass( w, h, shaderManager ); // add the pass to the renderer and activate it - renderer->addPass(pass, pass->index()); + renderer->addPass( pass, pass->index() ); pass->activate(); } //! [Adding a Z-only pass] - //! [Adding a wireframe pass] + //! [Adding a CustomAttribToColorPass pass] { - // this pass draw all the objects in wareframe, using the shared z-buffer for hidden line removal and drawing - // in the shared color buffer. - auto pass = std::make_shared<RadiumNBR::WireframePass>( renderer->allRenderObjects(), 2 ); + // this pass draw all the objects using the custom color function, + // Rendering is done against the shared z-buffer for hidden line removal and + // into the shared color buffer. + m_customPass = std::make_shared<RadiumNBR::CustomAttribToColorPass>( + renderer->allRenderObjects(), 2 ); // set the input/output textures - pass->setInputs( *depthtexture ); - pass->setOutput( *colortexture ); + m_customPass->setInputs( *depthtexture ); + m_customPass->setOutput( *colortexture ); + m_customPass->setLightManager( renderer->getLightManager() ); + m_customPass->setAttribToColorFunc( m_vertexFunction, m_fragmentFunction ); // configure access to shader/resources files and initialize the pass - pass->setResourcesDir( resourcesPath ); - pass->initializePass( w, h, shaderManager ); + m_customPass->setResourcesDir( resourcesPath ); + m_customPass->initializePass( w, h, shaderManager ); // add the pass to the renderer and activate it - renderer->addPass(pass, pass->index()); - pass->activate(); + renderer->addPass( m_customPass, m_customPass->index() ); + m_customPass->activate(); } - //! [Adding a wireframe pass] + //! [Adding a CustomAttribToColorPass pass] + } + + void update( const Ra::Engine::Data::ViewingParameters& ) override{ + /* This function is called once before each frame. + * You can use it to modify the scene, or modify the computation function of the renderer + * For this, just call + * m_customPass->setAttribToColorFunc( customVertexAttrib, customFragmentColor ); + * with the wanted shader glsl source code. + */ + }; + + void resize( int w, int h ) override{ + /* if you have resizeable resources in your controller, this method is called each time the + * viewer window is resized so that you can dimension your resources accordingly + */ + }; + + /// Set the custom glsl function for attrib management (vertex) and colorcomputation (fragment) + void setAttribToColorFunc( const std::string& vertex_source, + const std::string& fragment_source ) { + m_vertexFunction = vertex_source; + m_fragmentFunction = fragment_source; + if ( m_customPass ) + { m_customPass->setAttribToColorFunc( m_vertexFunction, m_fragmentFunction ); } } + + private: + /// The custom pass if needed for modification + std::shared_ptr<RadiumNBR::CustomAttribToColorPass> m_customPass; + + /// + std::string m_vertexFunction{"void outputCustomAttribs() {}"}; + std::string m_fragmentFunction{"vec4 computeCustomColor(Material mat, vec3 lightDir, vec3 " + "viewDir) {return vec4(1, 0, 0, 1);}"}; }; /** @@ -90,19 +150,142 @@ class RendererController : public RadiumNBR::RenderControlFunctor { class DemoWindowFactory : public Ra::Gui::BaseApplication::WindowFactory { public: - DemoWindowFactory() = delete; + DemoWindowFactory() = delete; ~DemoWindowFactory() = default; - explicit DemoWindowFactory( std::shared_ptr<RadiumNBR::NodeBasedRenderer> r) : renderer(r) {} + explicit DemoWindowFactory( std::shared_ptr<RadiumNBR::NodeBasedRenderer> r ) : renderer( r ) {} inline Ra::Gui::MainWindowInterface* createMainWindow() const override { auto window = new Ra::Gui::SimpleWindow(); - window->addRenderer(renderer->getRendererName(), renderer); + window->addRenderer( renderer->getRendererName(), renderer ); return window; } + private: std::shared_ptr<RadiumNBR::NodeBasedRenderer> renderer; }; +/** Process the scene to add the custom attribs to all objects + */ +void AddCustomAttributeToMeshes() { + auto entityManager = Ra::Engine::RadiumEngine::getInstance()->getEntityManager(); + auto entities = entityManager->getEntities(); + auto roManager = Ra::Engine::RadiumEngine::getInstance()->getRenderObjectManager(); + + for ( const auto e : entities ) + { + if ( e == Ra::Engine::Scene::SystemEntity::getInstance() ) { continue; } + for ( const auto& c : e->getComponents() ) + { + for ( const auto& roIdx : c->m_renderObjects ) + { + const auto& ro = roManager->getRenderObject( roIdx ); + if ( ro->getType() == Ra::Engine::Rendering::RenderObjectType::Geometry ) + { + auto theMesh = ro->getMesh(); + + /* Could be helpfull if one can do + auto displayable = dynamic_cast<Ra::Engine::Data::CoreGeometryDisplayable< ??? + >*>( theMesh.get() ); + */ + auto displayMesh = dynamic_cast<Ra::Engine::Data::Mesh*>( theMesh.get() ); + if ( displayMesh ) + { + LOG( logINFO ) << "\t\tMesh " << displayMesh->getName() + << " (TriangleMesh) is processed."; + displayMesh->addAttrib<Ra::Core::Vector4>( + "myCustomAttrib", + Ra::Core::Vector4Array{displayMesh->getNumVertices(), + Ra::Core::Utils::Color::Yellow()} ); + displayMesh->setDirty( "myCustomAttrib" ); + } + else + { + auto polyMesh = dynamic_cast<Ra::Engine::Data::PolyMesh*>( theMesh.get() ); + if ( polyMesh ) + { + LOG( logINFO ) << "\t\tMesh " << polyMesh->getName() + << " (PolyMesh) is processed."; + polyMesh->addAttrib<Ra::Core::Vector4>( + "myCustomAttrib", + Ra::Core::Vector4Array{polyMesh->getNumVertices(), + Ra::Core::Utils::Color::Magenta()} ); + polyMesh->setDirty( "myCustomAttrib" ); + } + else + { + auto pointCloud = + dynamic_cast<Ra::Engine::Data::PointCloud*>( theMesh.get() ); + if ( pointCloud ) + { + LOG( logINFO ) << "\t\tPointCloud " << pointCloud->getName() + << " is processed."; + pointCloud->addAttrib<Ra::Core::Vector4>( + "myCustomAttrib", + Ra::Core::Vector4Array{pointCloud->getNumVertices(), + Ra::Core::Utils::Color::Blue()} ); + pointCloud->setDirty( "myCustomAttrib" ); + } + else + { + LOG( logINFO ) << "\t\tMesh " << theMesh->getName() + << " is nor a trianglemesh, nor a polymesh, nor a " + "point cloud ... skipping"; + } + } + } + } + } + } + } +} + +/** + * The custom color computation shaders + */ + +/* Vertex shader : here, the objective is to transform any set ov vertex + * attribute to any set of fragment shaders input. + * In this example, we just copy the custom attrib to the output. + * All standards attribs can be accessed using their Radium name. + * If needed, all these vectors are expresed in the world frame : + * out_viewVector, out_lightVector, out_position, out_normal (if available) + * if available from the underlying geometry, the following parameters can be used : + * out_texcoord, out_vertexcolor. + * + * Note that this string could also be loaded from a file and/or manually edited if you + * add an edition widget as in Radium app demo + */ +const std::string customVertexAttrib{"in vec4 myCustomAttrib;\n" + "out vec4 fragCustomAttrib;\n" + "\nvoid outputCustomAttribs() { \n" + "fragCustomAttrib=myCustomAttrib;\n" + "}\n"}; +/* Fragment shader : here, the objective is to compute the final color of the fragment. + * This function get the Material associated with the objet. This material could be + * used through the Radium GLSL BSDF interface (see Radium doc) + * All standard interpolated attributes from vertices can be fetched using the Radium + * GLSL VertexAttribInterface (see Radium doc) + * The Light could be used through the Radium GLSL light interface. + * + * In the following exemple, the lighting is computed using the custom attrib as + * diffuse reflectance. + * + * Note that this string could also be loaded from a file and/or manually edited if you + * 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) {\n" + "vec3 diffColor; \n" + "vec3 specColor; \n" + "getSeparateBSDFComponent( mat, getPerVertexTexCoord(), lightDir, viewDir,\n" + "vec3(0, 0, 1), diffColor, specColor );\n" + "return vec4( (specColor+fragCustomAttrib.rgb)*max(lightDir.z, 0), 1); \n" + "}\n"}; + +/** + * main function. + */ int main( int argc, char* argv[] ) { if ( argc < 2 ) { @@ -112,6 +295,7 @@ int main( int argc, char* argv[] ) { //! [Instatiating the renderer giving a customization functor] RendererController renderControl; auto renderer = std::make_shared<RadiumNBR::NodeBasedRenderer>( renderControl ); + renderControl.setAttribToColorFunc( customVertexAttrib, customFragmentColor ); //! [Instatiating the renderer giving a customization functor] //! [Instatiating the application] @@ -123,5 +307,63 @@ int main( int argc, char* argv[] ) { app.initialize( DemoWindowFactory( renderer ) ); //! [Initializing the application] + //! [Processing the scene to define custom attributes] + AddCustomAttributeToMeshes(); + //! [Processing the scene to define custom attributes] + + return app.exec(); +} + +#if 0 +int main( int argc, char* argv[] ) { + + //! [Instatiating the renderer giving a customization functor] + RendererController renderControl; + auto renderer = std::make_shared<RadiumNBR::NodeBasedRenderer>( renderControl ); + + //! [Instatiating the renderer giving a customization functor] + + //! [Instatiating the application] + Ra::Gui::BaseApplication app( argc, argv ); + //! [Instatiating the application] + + //! [Initializing the application] + // The customization functor is called here + app.initialize( DemoWindowFactory( renderer ) ); + //! [Initializing the application] + + //! [show the base grid on the renderer] + renderer->showDebug( false ); + //! [show the base grid on the renderer] + + //! [Creating the cube] + auto cube = Ra::Core::Geometry::makeSharpBox( {0.1f, 0.1f, 0.1f} ); + //! [Creating the cube] + + //! [Colorize the Cube] + cube.addAttrib( + "myCustomAttrib", + Ra::Core::Vector4Array{cube.vertices().size(), Ra::Core::Utils::Color::Yellow()} ); + //! [Colorize the Cube] + + //! [Create the engine entity for the cube] + auto e = app.m_engine->getEntityManager()->createEntity( "Green cube" ); + //! [Create the engine entity for the cube] + + //! [Create a geometry component with the cube] + auto c = + new Ra::Engine::Scene::TriangleMeshComponent( "Cube Mesh", e, std::move( cube ), nullptr ); + //! [Create a geometry component with the cube] + + //! [Register the entity/component association to the geometry system ] + auto geometrySystem = app.m_engine->getSystem( "GeometrySystem" ); + geometrySystem->addComponent( e, c ); + //! [Register the entity/component association to the geometry system ] + + //! [Tell the window that something is to be displayed] + app.m_mainWindow->prepareDisplay(); + //! [Tell the window that something is to be displayed] + return app.exec(); } +#endif diff --git a/src/libRender/CMakeLists.txt b/src/libRender/CMakeLists.txt index fc67a72..7853fff 100644 --- a/src/libRender/CMakeLists.txt +++ b/src/libRender/CMakeLists.txt @@ -34,6 +34,7 @@ set(sources RadiumNBR/FullFeatureRenderer.cpp RadiumNBR/SphereSampler.cpp RadiumNBR/Passes/ClearPass.cpp + RadiumNBR/Passes/CustomAttribToColorPass.cpp RadiumNBR/Passes/DebugPass.cpp RadiumNBR/Passes/GeomPrepass.cpp RadiumNBR/Passes/AccessibilityBufferPass.cpp @@ -53,6 +54,7 @@ set(public_headers RadiumNBR/RenderPass.hpp RadiumNBR/SphereSampler.hpp RadiumNBR/Passes/ClearPass.hpp + RadiumNBR/Passes/CustomAttribToColorPass.hpp RadiumNBR/Passes/DebugPass.hpp RadiumNBR/Passes/GeomPrepass.hpp RadiumNBR/Passes/AccessibilityBufferPass.hpp diff --git a/src/libRender/RadiumNBR/NodeBasedRenderer.hpp b/src/libRender/RadiumNBR/NodeBasedRenderer.hpp index b7dfb13..0c7950d 100644 --- a/src/libRender/RadiumNBR/NodeBasedRenderer.hpp +++ b/src/libRender/RadiumNBR/NodeBasedRenderer.hpp @@ -88,6 +88,9 @@ class NodeBasedRenderer_LIBRARY_API NodeBasedRenderer : public Ra::Engine::Rende /// Hide or show the Debug objects void showDebug( bool b ); + /// Access the default light manager + Ra::Engine::Scene::LightManager* getLightManager() { return m_lightmanagers[0]; } + protected: void initializeInternal() override; void resizeInternal() override; diff --git a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp new file mode 100644 index 0000000..34674aa --- /dev/null +++ b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp @@ -0,0 +1,247 @@ +#include <RadiumNBR/Passes/CustomAttribToColorPass.hpp> + +#ifdef PASSES_LOG +# include <Core/Utils/Log.hpp> +using namespace Ra::Core::Utils; // log +#endif + +#include <Engine/Data/Material.hpp> +#include <Engine/Data/ShaderConfigFactory.hpp> +#include <Engine/Data/ShaderProgramManager.hpp> +#include <Engine/Data/Texture.hpp> +#include <Engine/Rendering/RenderObject.hpp> +#include <Engine/Scene/LightManager.hpp> + +#include <globjects/Framebuffer.h> + +namespace RadiumNBR { +using namespace gl; + +static const GLenum buffers[] = {GL_COLOR_ATTACHMENT0}; + +CustomAttribToColorPass::CustomAttribToColorPass( + const std::vector<RenderObjectPtr>* objectsToRender, + const Ra::Core::Utils::Index& idx ) : + RenderPass( "Custom AttribToColor pass", idx, objectsToRender ) {} + +CustomAttribToColorPass::~CustomAttribToColorPass() = default; + +bool CustomAttribToColorPass::initializePass( size_t /* width */, + size_t /* height */, + Ra::Engine::Data::ShaderProgramManager* shaderMngr ) { + m_shaderMngr = shaderMngr; + m_fbo = std::make_unique<globjects::Framebuffer>(); + + return true; +} + +void CustomAttribToColorPass::setInputs( const SharedTextures& depthBuffer ) { + addImportedTextures( {"CustomAtt2Clr::Depth", depthBuffer.second} ); +} + +void CustomAttribToColorPass::setOutput( const SharedTextures& colorBuffer ) { + m_outputTexture = colorBuffer; +} + +bool CustomAttribToColorPass::update() { + + return true; +} + +void CustomAttribToColorPass::resize( size_t width, size_t height ) { + + // Only resize the owned textures. Imported one are resized by their owner + for ( auto& t : m_localTextures ) + { + t.second->resize( width, height ); + } + + for ( auto& t : m_sharedTextures ) + { + t.second->resize( width, height ); + } + + m_fbo->bind(); + m_fbo->attachTexture( GL_DEPTH_ATTACHMENT, + m_importedTextures["CustomAtt2Clr::Depth"]->texture() ); + m_fbo->attachTexture( GL_COLOR_ATTACHMENT0, m_outputTexture.second->texture() ); +#ifdef PASSES_LOG + if ( m_fbo->checkStatus() != GL_FRAMEBUFFER_COMPLETE ) + { LOG( logERROR ) << "FBO Error (EmissivityPass::resize): " << m_fbo->checkStatus(); } +#endif + // finished with fbo, undbind to bind default + globjects::Framebuffer::unbind(); +} + +void CustomAttribToColorPass::execute( + const Ra::Engine::Data::ViewingParameters& viewParams ) const { + + m_fbo->bind(); + // only draw into 1 buffers (Color) + GL_ASSERT( glDrawBuffers( 1, buffers ) ); + GL_ASSERT( glDepthMask( GL_FALSE ) ); + GL_ASSERT( glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ) ); + + GL_ASSERT( glEnable( GL_DEPTH_TEST ) ); + GL_ASSERT( glDepthFunc( GL_LEQUAL ) ); + GL_ASSERT( glEnable( GL_BLEND ) ); + + glBlendFunc( GL_ONE, GL_ONE ); + // To prevent z-fighting, remove polygon offset + glDisable( GL_POLYGON_OFFSET_FILL ); + + 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 ); + + for ( const auto& ro : *m_objectsToRender ) + { + ro->render( passParams, viewParams, passIndex() ); + } + } + } + // need to maintain global state invariant + glEnable( GL_POLYGON_OFFSET_FILL ); + + GL_ASSERT( glDisable( GL_BLEND ) ); +} + +bool CustomAttribToColorPass::buildRenderTechnique( + const Ra::Engine::Rendering::RenderObject* ro, + Ra::Engine::Rendering::RenderTechnique& rt ) const { + if ( m_needConfigRebuild ) + { + m_needConfigRebuild = false; + Ra::Engine::Data::ShaderConfigurationFactory::removeConfiguration( + {"CustomAtt2ClrPass::CustomColorProgram"} ); + } + std::string resourcesRootDir = getResourcesDir(); + auto mat = const_cast<Ra::Engine::Rendering::RenderObject*>( ro )->getMaterial(); + // Volumes are not allowed here + if ( mat->getMaterialAspect() == Ra::Engine::Data::Material::MaterialAspect::MAT_DENSITY ) + { return false; } + if ( auto cfg = Ra::Engine::Data::ShaderConfigurationFactory::getConfiguration( + {"CustomAtt2ClrPass::CustomColorProgram"} ) ) + { rt.setConfiguration( *cfg, passIndex() ); } + else + { + // This part is the default configuration for this pass + // In this shader, the definition of the function void outputCustomAttrib(), along the + // declaration of input attribs and output result must be appended to the source. + // e.g. + // in vec3 attribName; + // out vec3 frag_attribName; + // void outputCustomAttrib() { + // frag_attribName = attribName; + // } + const std::string vertexShaderSource{ + "#include \"TransformStructs.glsl\"\n" + "#include \"DefaultLight.glsl\"\n" + "layout (location = 0) in vec3 in_position;\n" + "layout (location = 1) in vec3 in_normal;\n" + "layout (location = 2) in vec3 in_texcoord;\n" + "layout (location = 3) in vec3 in_vertexcolor;\n" + "#ifndef DONT_USE_INPUT_TANGENT\n" + "layout( location = 4 ) in vec3 in_tangent;\n" + "#endif\n" + "layout (location = 0) out vec3 out_position;\n" + "layout (location = 1) out vec3 out_normal;\n" + "layout (location = 2) out vec3 out_texcoord;\n" + "layout (location = 3) out vec3 out_vertexcolor;\n" + "#ifndef DONT_USE_INPUT_TANGENT\n" + "layout( location = 4 ) out vec3 out_tangent;\n" + "#endif\n" + "layout (location = 5) out vec3 out_viewVector;\n" + "layout (location = 6) out vec3 out_lightVector;\n" + "uniform Transform transform;\n" + "void outputCustomAttribs();" + "void main() {\n" + "mat4 mvp = transform.proj * transform.view * transform.model;\n" + "gl_Position = mvp * vec4(in_position, 1.0);\n" + "vec4 pos = transform.model * vec4(in_position, 1.0);\n" + "pos /= pos.w;\n" + "vec3 normal = mat3(transform.worldNormal) * in_normal;\n" + "vec3 tangent = mat3(transform.model) * in_tangent;\n" + "vec3 eye = -transform.view[3].xyz * mat3(transform.view);\n" + "out_position = vec3(pos);\n" + "out_normal = normal;\n" + "out_texcoord = in_texcoord;\n" + "out_vertexcolor = in_vertexcolor;\n" + "#ifndef DONT_USE_INPUT_TANGENT\n" + "out_tangent = tangent;\n" + "#endif\n" + "out_viewVector = vec3(eye - out_position);\n" + "out_lightVector = getLightDirection(light, out_position);\n" + "outputCustomAttribs();" + "}\n"}; + // the function computeCustomColor, alongside the declaration of frag_attrib is appended to + // the source + const std::string fragmentShadersource{ + "#include \"DefaultLight.glsl\"\n" + "#include \"VertexAttribInterface.frag.glsl\"\n" + "layout (location = 5) in vec3 in_viewVector;\n" + "layout (location = 6) in vec3 in_lightVector;\n" + "layout (location = 0) out vec4 out_color;\n" + "vec4 computeCustomColor(Material mat, vec3 light_dir, vec3 view_dir);\n" + "void main()\n" + "{\n" + "// All vectors are in world space\n" + "// A material is always evaluated in the fragment local Frame \n" + "// compute matrix from World to local Frame \n" + "vec3 normalWorld = getWorldSpaceNormal();// normalized interpolated normal \n" + "vec3 tangentWorld = getWorldSpaceTangent();// normalized tangent \n" + "vec3 binormalWorld = getWorldSpaceBiTangent();// normalized bitangent \n" + "// discard non opaque fragment \n" + "vec4 bc = getDiffuseColor(material, getPerVertexTexCoord()); \n" + "if (toDiscard(material, bc)) \n" + "discard; \n" + "// Apply normal mapping \n" + "normalWorld = getNormal(material, getPerVertexTexCoord(), \n" + "normalWorld, tangentWorld, binormalWorld);// normalized bump-mapped normal \n" + "binormalWorld = normalize(cross(normalWorld, tangentWorld));// normalized " + "tangent \n" + "tangentWorld = normalize(cross(binormalWorld, normalWorld));// normalized " + "bitangent \n" + "mat3 world2local; \n" + "world2local[0] = vec3(tangentWorld.x, binormalWorld.x, normalWorld.x); \n" + "world2local[1] = vec3(tangentWorld.y, binormalWorld.y, normalWorld.y); \n" + "world2local[2] = vec3(tangentWorld.z, binormalWorld.z, normalWorld.z); \n" + "// transform all vectors in local frame so that N = (0, 0, 1); \n" + "vec3 lightDir = normalize(world2local * in_lightVector);// incident direction \n" + "vec3 viewDir = normalize(world2local * in_viewVector);// outgoing direction\n" + "out_color = computeCustomColor(material, lightDir, viewDir);\n" + "}\n"}; + + Ra::Engine::Data::ShaderConfiguration theConfig{"CustomAtt2ClrPass::CustomColorProgram"}; + theConfig.addShaderSource( Ra::Engine::Data::ShaderType::ShaderType_VERTEX, + vertexShaderSource + m_customVertexAttrib ); + theConfig.addShaderSource( Ra::Engine::Data::ShaderType::ShaderType_FRAGMENT, + fragmentShadersource + m_customFragmentColor ); + theConfig.addInclude( "\"" + mat->getMaterialName() + ".glsl\"", + Ra::Engine::Data::ShaderType::ShaderType_FRAGMENT ); + // Add to the ShaderConfigManager + Ra::Engine::Data::ShaderConfigurationFactory::addConfiguration( theConfig ); + // Add to the RenderTechnique + rt.setConfiguration( theConfig, passIndex() ); + } + rt.setParametersProvider( mat, passIndex() ); + return true; +} + +void CustomAttribToColorPass::setAttribToColorFunc( const std::string& vertex_source, + const std::string& fragment_source ) { + m_customVertexAttrib = vertex_source; + m_customFragmentColor = fragment_source; + m_needConfigRebuild = true; +} + +void CustomAttribToColorPass::setLightManager( const Ra::Engine::Scene::LightManager* lm ) { + m_lightmanager = lm; +} +} // namespace RadiumNBR diff --git a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp new file mode 100644 index 0000000..59b0e2f --- /dev/null +++ b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp @@ -0,0 +1,66 @@ +#pragma once +#include <RadiumNBR/RenderPass.hpp> + +#include <Core/Utils/Color.hpp> + +namespace Ra::Engine::Scene { +class LightManager; +} + +namespace Ra::Engine::Data { +class ShaderProgram; +} // namespace Ra::Engine::Data + +namespace globjects { +class Framebuffer; +} + +namespace RadiumNBR { +/// Render pass that draws objects using a custom color computation function (in glsl) +class CustomAttribToColorPass : public RenderPass +{ + public: + CustomAttribToColorPass( const std::vector<RenderObjectPtr>* objectsToRender, + const Ra::Core::Utils::Index& idx ); + ~CustomAttribToColorPass() override; + + bool buildRenderTechnique( const Ra::Engine::Rendering::RenderObject* ro, + Ra::Engine::Rendering::RenderTechnique& rt ) const override; + bool initializePass( size_t width, + size_t height, + Ra::Engine::Data::ShaderProgramManager* shaderMngr ) override; + bool update() override; + void resize( size_t width, size_t height ) override; + void execute( const Ra::Engine::Data::ViewingParameters& viewParams ) const override; + + /// Add the output colorBuffer + void setOutput( const SharedTextures& colorBuffer ); + + /// These inputs must be computed before executing this pass : depth buffer + void setInputs( const SharedTextures& depthBuffer ); + + void setLightManager( const Ra::Engine::Scene::LightManager* lm ); + + /// Set the custom glsl function for attrib management (vertex) and colorcomputation (fragment) + void setAttribToColorFunc(const std::string &vertex_source, const std::string & fragment_source); + private: + /// The framebuffer used to render this pass + std::unique_ptr<globjects::Framebuffer> m_fbo{nullptr}; + + /// The Shader manager to use when building shaders + Ra::Engine::Data::ShaderProgramManager* m_shaderMngr{nullptr}; + + /// The color texture for output.Stored here for easy access. + SharedTextures m_outputTexture; + + /// The custom Attrib Vertex shader function + std::string m_customVertexAttrib{}; + /// the custom FragmentColor shader function + std::string m_customFragmentColor{}; + /// State indicator + mutable bool m_needConfigRebuild{true}; + + /// The light manager to use + const Ra::Engine::Scene::LightManager* m_lightmanager; +}; +} // namespace RadiumNBR -- GitLab