diff --git a/Changelog.md b/Changelog.md index 0082c5f50..8878ad5f6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -56,6 +56,48 @@ ### Ignition Rendering 4.X +### Ignition Rendering 4.8.0 (2021-06-18) + +1. relax gaussian test tolerance + * [Pull request #344](https://github.com/ignitionrobotics/ign-rendering/pull/344) + +1. recreate node only when needed + * [Pull request #342](https://github.com/ignitionrobotics/ign-rendering/pull/342) + +1. Backport memory fixes found by ASAN + * [Pull request #340](https://github.com/ignitionrobotics/ign-rendering/pull/340) + +1. Re-enable part of depth camera test on macOS + * [Pull request #335](https://github.com/ignitionrobotics/ign-rendering/pull/335) + * [Pull request #347](https://github.com/ignitionrobotics/ign-rendering/pull/347) + +1. Fix depth alpha + * [Pull request #316](https://github.com/ignitionrobotics/ign-rendering/pull/316) + +1. Fix floating point precision bug handling alpha channel (#332) + * [Pull request #333](https://github.com/ignitionrobotics/ign-rendering/pull/333) + +1. Include MoveTo Helper class to ign-rendering + * [Pull request #311](https://github.com/ignitionrobotics/ign-rendering/pull/311) + +1. Remove `tools/code_check` and update codecov + * [Pull request #321](https://github.com/ignitionrobotics/ign-rendering/pull/321) + +1. [OGRE 1.x] Uniform buffer shader support + * [Pull request #294](https://github.com/ignitionrobotics/ign-rendering/pull/294) + +1. Helper function to get a scene + * [Pull request #320](https://github.com/ignitionrobotics/ign-rendering/pull/320) + +1. Reduce lidar data discretization + * [Pull request #296](https://github.com/ignitionrobotics/ign-rendering/pull/296) + +1. Prevent console warnings when multiple texture coordinates are present + * [Pull request #301](https://github.com/ignitionrobotics/ign-rendering/pull/301) + +1. Added command line argument to pick version of Ogre + * [Pull request #277](https://github.com/ignitionrobotics/ign-rendering/pull/277) + ### Ignition Rendering 4.7.0 (2021-03-17) 1. Enable depth write in particles example diff --git a/examples/custom_shaders_uniforms/CMakeLists.txt b/examples/custom_shaders_uniforms/CMakeLists.txt index 9909e78be..5fc59e542 100644 --- a/examples/custom_shaders_uniforms/CMakeLists.txt +++ b/examples/custom_shaders_uniforms/CMakeLists.txt @@ -5,7 +5,7 @@ include_directories(SYSTEM ${PROJECT_BINARY_DIR} ) -find_package(ignition-rendering4) +find_package(ignition-rendering6) set(TARGET_THIRD_PARTY_DEPENDS "") diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2Includes.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2Includes.hh index cbc981493..27d360bf7 100644 --- a/ogre2/include/ignition/rendering/ogre2/Ogre2Includes.hh +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2Includes.hh @@ -92,7 +92,6 @@ #include #include #include -#include #include #include #include diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTarget.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTarget.hh index 30d7ed73d..439beafac 100644 --- a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTarget.hh +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTarget.hh @@ -115,9 +115,36 @@ namespace ignition /// \see Camera::SetShadowsNodeDefDirty public: void SetShadowsNodeDefDirty(); - /// \brief Get a pointer to the ogre render target + /// \brief Returns the FSAA to use based on supported specs by HW + /// and value specified in Ogre2RenderTarget::AntiAliasing + /// \return Value in range [1; 256). 1 means no antialiasing. + protected: uint8_t TargetFSAA() const; + + /// \brief Get a pointer to the ogre render target containing + /// the results of the render (implemented separately + /// to avoid breaking ABI of the pure virtual function) + protected: Ogre::TextureGpu *RenderTargetImpl() const; + + /// \brief Get a pointer to the ogre render target containing + /// the results of the render public: virtual Ogre::TextureGpu *RenderTarget() const = 0; + /// \brief Returns true if this is a render window + /// TODO(anyone): this function should be virtual. + /// We didn't do it to preserve ABI. + /// Look in commit history for '#Ogre2IsRenderWindowABI' to + /// see changes made and revert + public: bool IsRenderWindow() const; + + // Documentation inherited + public: unsigned int GLIdImpl() const; + + /// \brief Destroy the render texture + protected: void DestroyTargetImpl(); + + /// \brief Build the render texture + protected: void BuildTargetImpl(); + /// \brief Get visibility mask for the viewport associated with this /// render target /// \return Visibility mask @@ -133,7 +160,10 @@ namespace ignition Ogre::CompositorWorkspace *_workspace, const std::string &_workspaceDefName, const std::string &_baseNode, const std::string &_finalNode, - const std::vector &_renderPasses, bool _recreateNodes); + const std::vector &_renderPasses, + bool _recreateNodes, + Ogre::TextureGpu *(*_ogreTextures)[2], + bool _isRenderWindow); /// \brief Update the background color protected: virtual void UpdateBackgroundColor(); @@ -144,9 +174,6 @@ namespace ignition /// \brief Update the render pass chain protected: virtual void UpdateRenderPassChain(); - /// \brief Deprecated. Use Ogre2Scene:UpdateShadowNode instead - protected: void IGN_DEPRECATED(5) UpdateShadowNode(); - /// \brief Implementation of the Rebuild function protected: virtual void RebuildImpl() override; @@ -229,7 +256,9 @@ namespace ignition // Documentation inherited public: virtual unsigned int GLId() const override; - // Documentation inherited. + // Documentation inherited + // TODO(anyone): this function should be removed. + // We didn't do it to preserve ABI. public: virtual Ogre::TextureGpu *RenderTarget() const override; // Documentation inherited. @@ -241,9 +270,6 @@ namespace ignition /// \brief Build the render texture protected: virtual void BuildTarget(); - /// \brief Pointer to the internal ogre render texture object - protected: Ogre::TextureGpu *ogreTexture = nullptr; - /// \brief Make scene our friend so it can create a ogre2 render texture private: friend class Ogre2Scene; }; @@ -261,6 +287,12 @@ namespace ignition // Documentation inherited. public: virtual void Destroy() override; + // TODO(anyone): this function should be virtual. + // We didn't do it to preserve ABI. + // Looks in commit history for '#Ogre2IsRenderWindowABI' to + // see changes made and revert + public: bool IsRenderWindow() const; + // Documentation inherited. public: virtual Ogre::TextureGpu *RenderTarget() const override; diff --git a/ogre2/src/Ogre2DepthCamera.cc b/ogre2/src/Ogre2DepthCamera.cc index 85b025df3..6068d83a8 100644 --- a/ogre2/src/Ogre2DepthCamera.cc +++ b/ogre2/src/Ogre2DepthCamera.cc @@ -104,7 +104,7 @@ class ignition::rendering::Ogre2DepthCameraPrivate public: Ogre::CompositorWorkspace *ogreCompositorWorkspace = nullptr; /// \brief Output texture with depth and color data - public: Ogre::TextureGpu *ogreDepthTexture = nullptr; + public: Ogre::TextureGpu *ogreDepthTexture[2]; /// \brief Dummy render texture for the depth data public: RenderTexturePtr depthTexture; @@ -225,15 +225,14 @@ void Ogre2DepthGaussianNoisePass::CreateRenderPass() nodeDef->setNumTargetPass(1); Ogre::CompositorTargetDef *inputTargetDef = nodeDef->addTargetPass("rt_output"); - inputTargetDef->setNumPasses(2); + inputTargetDef->setNumPasses(1); { - // clear pass - inputTargetDef->addPass(Ogre::PASS_CLEAR); - // quad pass Ogre::CompositorPassQuadDef *passQuad = static_cast( inputTargetDef->addPass(Ogre::PASS_QUAD)); + passQuad->setAllLoadActions(Ogre::LoadAction::Clear); + passQuad->mMaterialName = materialName; passQuad->addQuadTextureSource(0, "rt_input"); } @@ -297,11 +296,14 @@ void Ogre2DepthCamera::Destroy() Ogre::CompositorManager2 *ogreCompMgr = ogreRoot->getCompositorManager2(); // remove depth texture, material, compositor - if (this->dataPtr->ogreDepthTexture) + for (size_t i = 0u; i < 2u; ++i) { - ogreRoot->getRenderSystem()->getTextureGpuManager()->destroyTexture( - this->dataPtr->ogreDepthTexture); - this->dataPtr->ogreDepthTexture = nullptr; + if (this->dataPtr->ogreDepthTexture[i]) + { + ogreRoot->getRenderSystem()->getTextureGpuManager()->destroyTexture( + this->dataPtr->ogreDepthTexture[i]); + this->dataPtr->ogreDepthTexture[i] = nullptr; + } } if (this->dataPtr->ogreCompositorWorkspace) { @@ -587,41 +589,11 @@ void Ogre2DepthCamera::CreateDepthTexture() this->dataPtr->ogreCompositorBaseNodeDef = baseNodeDefName; Ogre::CompositorNodeDef *baseNodeDef = ogreCompMgr->addNodeDefinition(baseNodeDefName); - Ogre::TextureDefinitionBase::TextureDefinition *rt0TexDef = - baseNodeDef->addTextureDefinition("rt0"); - rt0TexDef->textureType = Ogre::TextureTypes::Type2D; - rt0TexDef->width = 0; - rt0TexDef->height = 0; - rt0TexDef->depthOrSlices = 1; - rt0TexDef->numMipmaps = 0; - rt0TexDef->widthFactor = 1; - rt0TexDef->heightFactor = 1; - rt0TexDef->format = Ogre::PFG_RGBA32_FLOAT; - rt0TexDef->textureFlags &= ~Ogre::TextureFlags::Uav; - rt0TexDef->depthBufferId = Ogre::DepthBuffer::POOL_INVALID; - rt0TexDef->depthBufferFormat = Ogre::PFG_UNKNOWN; - rt0TexDef->fsaa = "0"; - - Ogre::RenderTargetViewDef *rtv0 = baseNodeDef->addRenderTextureView("rt0"); - rtv0->setForTextureDefinition("rt0", rt0TexDef); - - Ogre::TextureDefinitionBase::TextureDefinition *rt1TexDef = - baseNodeDef->addTextureDefinition("rt1"); - rt1TexDef->textureType = Ogre::TextureTypes::Type2D; - rt1TexDef->width = 0; - rt1TexDef->height = 0; - rt1TexDef->depthOrSlices = 1; - rt1TexDef->numMipmaps = 0; - rt1TexDef->widthFactor = 1; - rt1TexDef->heightFactor = 1; - rt1TexDef->format = Ogre::PFG_RGBA32_FLOAT; - rt1TexDef->textureFlags &= ~Ogre::TextureFlags::Uav; - rt1TexDef->depthBufferId = Ogre::DepthBuffer::POOL_INVALID; - rt1TexDef->depthBufferFormat = Ogre::PFG_UNKNOWN; - rt1TexDef->fsaa = "0"; - - Ogre::RenderTargetViewDef *rtv1 = baseNodeDef->addRenderTextureView("rt1"); - rtv1->setForTextureDefinition("rt1", rt1TexDef); + + baseNodeDef->addTextureSourceName( + "rt0", 0u, Ogre::TextureDefinitionBase::TEXTURE_INPUT); + baseNodeDef->addTextureSourceName( + "rt1", 1u, Ogre::TextureDefinitionBase::TEXTURE_INPUT); Ogre::TextureDefinitionBase::TextureDefinition *depthTexDef = baseNodeDef->addTextureDefinition("depthTexture"); @@ -710,17 +682,10 @@ void Ogre2DepthCamera::CreateDepthTexture() baseNodeDef->addTargetPass("colorTexture"); if (validBackground) - colorTargetDef->setNumPasses(3); - else colorTargetDef->setNumPasses(2); + else + colorTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - colorTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue( - Ogre2Conversions::Convert(this->Scene()->BackgroundColor()))); - if (validBackground) { // quad pass @@ -731,6 +696,10 @@ void Ogre2DepthCamera::CreateDepthTexture() + this->Name(); passQuad->mFrustumCorners = Ogre::CompositorPassQuadDef::CAMERA_DIRECTION; + + passQuad->setAllLoadActions(Ogre::LoadAction::Clear); + passQuad->setAllClearColours(Ogre::ColourValue( + Ogre2Conversions::Convert(this->Scene()->BackgroundColor()))); } // scene pass @@ -742,25 +711,28 @@ void Ogre2DepthCamera::CreateDepthTexture() // todo(anyone) PbsMaterialsShadowNode is hardcoded. // Although this may be just fine passScene->mShadowNode = "PbsMaterialsShadowNode"; + + if (!validBackground) + { + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(Ogre::ColourValue( + Ogre2Conversions::Convert(this->Scene()->BackgroundColor()))); + } } Ogre::CompositorTargetDef *depthTargetDef = baseNodeDef->addTargetPass("depthTexture"); - depthTargetDef->setNumPasses(2); + depthTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - depthTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue( - this->FarClipPlane(), - this->FarClipPlane(), - this->FarClipPlane())); - // scene pass Ogre::CompositorPassSceneDef *passScene = static_cast( depthTargetDef->addPass(Ogre::PASS_SCENE)); + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(Ogre::ColourValue( + this->FarClipPlane(), + this->FarClipPlane(), + this->FarClipPlane())); // depth texute does not contain particles passScene->mVisibilityMask = IGN_VISIBILITY_ALL & ~Ogre2ParticleEmitter::kParticleVisibilityFlags; @@ -768,39 +740,31 @@ void Ogre2DepthCamera::CreateDepthTexture() Ogre::CompositorTargetDef *particleTargetDef = baseNodeDef->addTargetPass("particleTexture"); - particleTargetDef->setNumPasses(2); + particleTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - particleTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue::Black); - // scene pass Ogre::CompositorPassSceneDef *passScene = static_cast( particleTargetDef->addPass(Ogre::PASS_SCENE)); + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(Ogre::ColourValue::Black); passScene->mVisibilityMask = Ogre2ParticleEmitter::kParticleVisibilityFlags; } Ogre::CompositorTargetDef *particleDepthTargetDef = baseNodeDef->addTargetPass("particleDepthTexture"); - particleDepthTargetDef->setNumPasses(2); + particleDepthTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - particleDepthTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue( - this->FarClipPlane(), - this->FarClipPlane(), - this->FarClipPlane())); - // scene pass Ogre::CompositorPassSceneDef *passScene = static_cast( particleDepthTargetDef->addPass(Ogre::PASS_SCENE)); + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(Ogre::ColourValue( + this->FarClipPlane(), + this->FarClipPlane(), + this->FarClipPlane())); passScene->mVisibilityMask = Ogre2ParticleEmitter::kParticleVisibilityFlags; } @@ -808,19 +772,18 @@ void Ogre2DepthCamera::CreateDepthTexture() // rt0 target - converts depth to xyz Ogre::CompositorTargetDef *inTargetDef = baseNodeDef->addTargetPass("rt0"); - inTargetDef->setNumPasses(2); + inTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - inTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue(this->FarClipPlane(), - this->FarClipPlane(), this->FarClipPlane())); - // quad pass Ogre::CompositorPassQuadDef *passQuad = static_cast( inTargetDef->addPass(Ogre::PASS_QUAD)); + passQuad->setAllLoadActions(Ogre::LoadAction::Clear); + passQuad->setAllClearColours(Ogre::ColourValue( + this->FarClipPlane(), + this->FarClipPlane(), + this->FarClipPlane())); + passQuad->mMaterialName = this->dataPtr->depthMaterial->getName(); passQuad->addQuadTextureSource(0, "depthTexture"); passQuad->addQuadTextureSource(1, "colorTexture"); @@ -860,32 +823,28 @@ void Ogre2DepthCamera::CreateDepthTexture() Ogre::CompositorNodeDef *finalNodeDef = ogreCompMgr->addNodeDefinition(finalNodeDefName); - // output texture - finalNodeDef->addTextureSourceName("rt_output", 0, + finalNodeDef->addTextureSourceName("rt_input", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT); - finalNodeDef->addTextureSourceName("rt_input", 1, + // output texture + finalNodeDef->addTextureSourceName("rt_output", 1, Ogre::TextureDefinitionBase::TEXTURE_INPUT); finalNodeDef->setNumTargetPass(1); // rt_output target - converts depth to xyz Ogre::CompositorTargetDef *outputTargetDef = finalNodeDef->addTargetPass("rt_output"); - outputTargetDef->setNumPasses(2); + outputTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - outputTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours( - Ogre::ColourValue( - this->FarClipPlane(), - this->FarClipPlane(), - this->FarClipPlane())); - // quad pass Ogre::CompositorPassQuadDef *passQuad = static_cast( outputTargetDef->addPass(Ogre::PASS_QUAD)); + passQuad->setAllLoadActions(Ogre::LoadAction::Clear); + passQuad->setAllClearColours(Ogre::ColourValue( + this->FarClipPlane(), + this->FarClipPlane(), + this->FarClipPlane())); + passQuad->mMaterialName = this->dataPtr->depthFinalMaterial->getName(); passQuad->addQuadTextureSource(0, "rt_input"); } @@ -902,8 +861,9 @@ void Ogre2DepthCamera::CreateDepthTexture() Ogre::CompositorWorkspaceDef *workDef = ogreCompMgr->addWorkspaceDefinition(wsDefName); - workDef->connect(baseNodeDefName, 0, finalNodeDefName, 1); - workDef->connectExternal(0, finalNodeDefName, 0); + workDef->connectExternal(0, baseNodeDefName, 0); + workDef->connectExternal(1, baseNodeDefName, 1); + workDef->connect(baseNodeDefName, finalNodeDefName); } Ogre::CompositorWorkspaceDef *wsDef = ogreCompMgr->getWorkspaceDefinition(wsDefName); @@ -917,23 +877,26 @@ void Ogre2DepthCamera::CreateDepthTexture() Ogre::TextureGpuManager *textureMgr = ogreRoot->getRenderSystem()->getTextureGpuManager(); // create render texture - these textures pack the range data - this->dataPtr->ogreDepthTexture = - textureMgr->createOrRetrieveTexture( - this->Name() + "_depth", - Ogre::GpuPageOutStrategy::SaveToSystemRam, - Ogre::TextureFlags::RenderToTexture, - Ogre::TextureTypes::Type2D); - - this->dataPtr->ogreDepthTexture->setResolution( - this->ImageWidth(), this->ImageHeight()); - this->dataPtr->ogreDepthTexture->setNumMipmaps(1u); - this->dataPtr->ogreDepthTexture->setPixelFormat( - Ogre::PFG_RGBA32_FLOAT); - - this->dataPtr->ogreDepthTexture->scheduleTransitionTo( - Ogre::GpuResidency::Resident); - - this->CreateWorkspaceInstance(); + for (size_t i = 0u; i < 2u; ++i) + { + this->dataPtr->ogreDepthTexture[i] = + textureMgr->createTexture( + this->Name() + "_depth" + std::to_string(i), + Ogre::GpuPageOutStrategy::SaveToSystemRam, + Ogre::TextureFlags::RenderToTexture, + Ogre::TextureTypes::Type2D); + + this->dataPtr->ogreDepthTexture[i]->setResolution( + this->ImageWidth(), this->ImageHeight()); + this->dataPtr->ogreDepthTexture[i]->setNumMipmaps(1u); + this->dataPtr->ogreDepthTexture[i]->setPixelFormat( + Ogre::PFG_RGBA32_FLOAT); + + this->dataPtr->ogreDepthTexture[i]->scheduleTransitionTo( + Ogre::GpuResidency::Resident); + } + + CreateWorkspaceInstance(); } ////////////////////////////////////////////////// @@ -943,11 +906,16 @@ void Ogre2DepthCamera::CreateWorkspaceInstance() auto ogreRoot = engine->OgreRoot(); Ogre::CompositorManager2 *ogreCompMgr = ogreRoot->getCompositorManager2(); + Ogre::CompositorChannelVec externalTargets(2u); + + externalTargets[0] = this->dataPtr->ogreDepthTexture[0]; + externalTargets[1] = this->dataPtr->ogreDepthTexture[1]; + // create compositor worksspace this->dataPtr->ogreCompositorWorkspace = ogreCompMgr->addWorkspace( this->scene->OgreSceneManager(), - this->dataPtr->ogreDepthTexture, + externalTargets, this->ogreCamera, this->dataPtr->ogreCompositorWorkspaceDef, false); @@ -966,7 +934,8 @@ void Ogre2DepthCamera::CreateWorkspaceInstance() this->dataPtr->particleNoiseListener.reset( new Ogre2ParticleNoiseListener(this->scene, this->dataPtr->depthMaterial)); - this->ogreCamera->addListener(this->dataPtr->particleNoiseListener.get()); + this->ogreCamera->addListener( + this->dataPtr->particleNoiseListener.get()); break; } } @@ -985,7 +954,7 @@ void Ogre2DepthCamera::Render() ////////////////////////////////////////////////// void Ogre2DepthCamera::PreRender() { - if (!this->dataPtr->ogreDepthTexture) + if (!this->dataPtr->ogreDepthTexture[0]) this->CreateDepthTexture(); if (!this->dataPtr->ogreCompositorWorkspace) @@ -998,7 +967,10 @@ void Ogre2DepthCamera::PreRender() this->dataPtr->ogreCompositorBaseNodeDef, this->dataPtr->ogreCompositorFinalNodeDef, this->dataPtr->renderPasses, - this->dataPtr->renderPassDirty); + this->dataPtr->renderPassDirty, + &this->dataPtr->ogreDepthTexture, + false); + for (auto &pass : this->dataPtr->renderPasses) pass->PreRender(); @@ -1043,7 +1015,7 @@ void Ogre2DepthCamera::PostRender() unsigned int bytesPerChannel = PixelUtil::BytesPerChannel(format); Ogre::Image2 image; - image.convertFromTexture(this->dataPtr->ogreDepthTexture, 0u, 0u); + image.convertFromTexture(this->dataPtr->ogreDepthTexture[1], 0u, 0u); Ogre::TextureBox box = image.getData(0); float *depthBufferTmp = static_cast(box.data); if (!this->dataPtr->depthBuffer) diff --git a/ogre2/src/Ogre2GpuRays.cc b/ogre2/src/Ogre2GpuRays.cc index 5624d4136..1006d5f18 100644 --- a/ogre2/src/Ogre2GpuRays.cc +++ b/ogre2/src/Ogre2GpuRays.cc @@ -40,7 +40,6 @@ #endif #include #include -#include #include #include #include @@ -861,17 +860,14 @@ void Ogre2GpuRays::Setup1stPass() Ogre::CompositorTargetDef *colorTargetDef = nodeDef->addTargetPass("colorTexture"); - colorTargetDef->setNumPasses(2); + colorTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - colorTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue(0, 0, 0)); // scene pass Ogre::CompositorPassSceneDef *passScene = static_cast( colorTargetDef->addPass(Ogre::PASS_SCENE)); + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(Ogre::ColourValue(0, 0, 0)); // set camera custom visibility mask when rendering laser retro passScene->mVisibilityMask = 0x01000000 & ~Ogre2ParticleEmitter::kParticleVisibilityFlags; @@ -879,21 +875,17 @@ void Ogre2GpuRays::Setup1stPass() Ogre::CompositorTargetDef *depthTargetDef = nodeDef->addTargetPass("depthTexture"); - depthTargetDef->setNumPasses(2); + depthTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - depthTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue( - this->FarClipPlane(), - this->FarClipPlane(), - this->FarClipPlane())); - // scene pass Ogre::CompositorPassSceneDef *passScene = static_cast( depthTargetDef->addPass(Ogre::PASS_SCENE)); + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(Ogre::ColourValue( + this->FarClipPlane(), + this->FarClipPlane(), + this->FarClipPlane())); // depth texture does not contain particles passScene->mVisibilityMask = 0x01000000 & ~Ogre2ParticleEmitter::kParticleVisibilityFlags; @@ -901,20 +893,17 @@ void Ogre2GpuRays::Setup1stPass() Ogre::CompositorTargetDef *particleDepthTargetDef = nodeDef->addTargetPass("particleDepthTexture"); - particleDepthTargetDef->setNumPasses(2); + particleDepthTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - particleDepthTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue( - this->FarClipPlane(), - this->FarClipPlane(), - this->FarClipPlane())); // scene pass Ogre::CompositorPassSceneDef *passScene = static_cast( particleDepthTargetDef->addPass(Ogre::PASS_SCENE)); + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(Ogre::ColourValue( + this->FarClipPlane(), + this->FarClipPlane(), + this->FarClipPlane())); // set camera custom visibility mask when rendering laser retro passScene->mVisibilityMask = Ogre2ParticleEmitter::kParticleVisibilityFlags; @@ -922,17 +911,14 @@ void Ogre2GpuRays::Setup1stPass() Ogre::CompositorTargetDef *particleTargetDef = nodeDef->addTargetPass("particleTexture"); - particleTargetDef->setNumPasses(2); + particleTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - particleTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue::Black); // scene pass Ogre::CompositorPassSceneDef *passScene = static_cast( particleTargetDef->addPass(Ogre::PASS_SCENE)); + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(Ogre::ColourValue::Black); // set camera custom visibility mask when rendering laser retro passScene->mVisibilityMask = Ogre2ParticleEmitter::kParticleVisibilityFlags; @@ -941,18 +927,16 @@ void Ogre2GpuRays::Setup1stPass() // rt_input target - converts depth to range Ogre::CompositorTargetDef *inputTargetDef = nodeDef->addTargetPass("rt_input"); - inputTargetDef->setNumPasses(2); + inputTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - inputTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue( - this->dataMaxVal, 0, 1.0)); // quad pass Ogre::CompositorPassQuadDef *passQuad = static_cast( inputTargetDef->addPass(Ogre::PASS_QUAD)); + passQuad->setAllLoadActions(Ogre::LoadAction::Clear); + passQuad->setAllClearColours(Ogre::ColourValue( + this->dataMaxVal, 0, 1.0)); + passQuad->mMaterialName = this->dataPtr->matFirstPass->getName(); passQuad->addQuadTextureSource(0, "depthTexture"); passQuad->addQuadTextureSource(1, "colorTexture"); @@ -1156,17 +1140,15 @@ void Ogre2GpuRays::Setup2ndPass() { Ogre::CompositorTargetDef *inputTargetDef = nodeDef->addTargetPass("rt_input"); - inputTargetDef->setNumPasses(2); - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - inputTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue( - this->dataMaxVal, 0, 1.0)); + inputTargetDef->setNumPasses(1); + // quad pass - sample from cubemap textures Ogre::CompositorPassQuadDef *passQuad = static_cast( inputTargetDef->addPass(Ogre::PASS_QUAD)); + passQuad->setAllLoadActions(Ogre::LoadAction::Clear); + passQuad->setAllClearColours(Ogre::ColourValue( + this->dataMaxVal, 0, 1.0)); passQuad->mMaterialName = this->dataPtr->matSecondPass->getName(); } nodeDef->mapOutputChannel(0, "rt_input"); diff --git a/ogre2/src/Ogre2RenderTarget.cc b/ogre2/src/Ogre2RenderTarget.cc index 975c21d67..7cde94f84 100644 --- a/ogre2/src/Ogre2RenderTarget.cc +++ b/ogre2/src/Ogre2RenderTarget.cc @@ -91,6 +91,14 @@ class ignition::rendering::Ogre2RenderTargetPrivate /// \brief Name of shadow compositor node public: const std::string kShadowNodeName = "PbsMaterialsShadowNode"; + + /// \brief Pointer to the internal ogre render texture objects + /// There's two because we ping pong postprocessing effects + /// and the final result is always in ogreTexture[1] + /// RenderWindows may have a 3rd texture which is the + /// actual window + /// + public: Ogre::TextureGpu *ogreTexture[2] = {nullptr, nullptr}; }; using namespace ignition; @@ -144,55 +152,43 @@ void Ogre2RenderTarget::BuildCompositor() Ogre::CompositorNodeDef *nodeDef = ogreCompMgr->addNodeDefinition(nodeDefName); - // Input texture - Ogre::TextureDefinitionBase::TextureDefinition *rt0TexDef = - nodeDef->addTextureDefinition("rt0"); - rt0TexDef->textureType = Ogre::TextureTypes::Type2D; - rt0TexDef->width = 0; - rt0TexDef->height = 0; - rt0TexDef->depthOrSlices = 1; - rt0TexDef->numMipmaps = 0; - rt0TexDef->widthFactor = 1; - rt0TexDef->heightFactor = 1; - rt0TexDef->format = Ogre::PFG_RGBA8_UNORM_SRGB; - rt0TexDef->textureFlags &= ~Ogre::TextureFlags::Uav; - rt0TexDef->depthBufferId = Ogre::DepthBuffer::POOL_DEFAULT; - rt0TexDef->depthBufferFormat = Ogre::PFG_UNKNOWN; - - Ogre::RenderTargetViewDef *rtv = nodeDef->addRenderTextureView("rt0"); - rtv->setForTextureDefinition("rt0", rt0TexDef); - - Ogre::TextureDefinitionBase::TextureDefinition *rt1TexDef = - nodeDef->addTextureDefinition("rt1"); - rt1TexDef->textureType = Ogre::TextureTypes::Type2D; - rt1TexDef->width = 0; - rt1TexDef->height = 0; - rt1TexDef->depthOrSlices = 1; - rt1TexDef->widthFactor = 1; - rt1TexDef->heightFactor = 1; - rt1TexDef->format = Ogre::PFG_RGBA8_UNORM_SRGB; - rt1TexDef->textureFlags &= ~Ogre::TextureFlags::Uav; - rt1TexDef->depthBufferId = Ogre::DepthBuffer::POOL_DEFAULT; - rt1TexDef->depthBufferFormat = Ogre::PFG_UNKNOWN; - - Ogre::RenderTargetViewDef *rtv2 = nodeDef->addRenderTextureView("rt1"); - rtv2->setForTextureDefinition("rt1", rt1TexDef); + nodeDef->addTextureSourceName( + "rt0", 0u, Ogre::TextureDefinitionBase::TEXTURE_INPUT); + nodeDef->addTextureSourceName( + "rt1", 1u, Ogre::TextureDefinitionBase::TEXTURE_INPUT); + + { + // Add a manually-defined RTV (based on an automatically generated one) + // so that we can perform an explicit MSAA resolve. + const Ogre::RenderTargetViewDef *rt0Def = + nodeDef->getRenderTargetViewDef( "rt0" ); + Ogre::RenderTargetViewDef *rtvDef = + nodeDef->addRenderTextureView( "rtv" ); + + *rtvDef = *rt0Def; + + const uint8_t fsaa = TargetFSAA(); + if (fsaa > 1u) + { + Ogre::TextureDefinitionBase::TextureDefinition *msaaDef = + nodeDef->addTextureDefinition("rt_fsaa"); + + msaaDef->fsaa = std::to_string(fsaa); + + rtvDef->colourAttachments[0].textureName = "rt_fsaa"; + rtvDef->colourAttachments[0].resolveTextureName = "rt0"; + } + } nodeDef->setNumTargetPass(2); Ogre::CompositorTargetDef *rt0TargetDef = - nodeDef->addTargetPass("rt0"); + nodeDef->addTargetPass("rtv"); if (validBackground) - rt0TargetDef->setNumPasses(3); - else rt0TargetDef->setNumPasses(2); + else + rt0TargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - rt0TargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(this->ogreBackgroundColor); - if (validBackground) { // quad pass @@ -203,6 +199,9 @@ void Ogre2RenderTarget::BuildCompositor() + this->Name(); passQuad->mFrustumCorners = Ogre::CompositorPassQuadDef::CAMERA_DIRECTION; + + passQuad->setAllLoadActions(Ogre::LoadAction::Clear); + passQuad->setAllClearColours(this->ogreBackgroundColor); } // scene pass @@ -211,6 +210,12 @@ void Ogre2RenderTarget::BuildCompositor() rt0TargetDef->addPass(Ogre::PASS_SCENE)); passScene->mShadowNode = this->dataPtr->kShadowNodeName; passScene->mIncludeOverlays = true; + + if (!validBackground) + { + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(this->ogreBackgroundColor); + } } nodeDef->mapOutputChannel(0, "rt0"); @@ -221,9 +226,9 @@ void Ogre2RenderTarget::BuildCompositor() this->dataPtr->kFinalNodeName; Ogre::CompositorNodeDef *finalNodeDef = ogreCompMgr->addNodeDefinition(finalNodeDefName); - finalNodeDef->addTextureSourceName("rt_output", 0, + finalNodeDef->addTextureSourceName("rtN", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT); - finalNodeDef->addTextureSourceName("rtN", 1, + finalNodeDef->addTextureSourceName("rt_output", 1, Ogre::TextureDefinitionBase::TEXTURE_INPUT); finalNodeDef->setNumTargetPass(2); @@ -246,21 +251,42 @@ void Ogre2RenderTarget::BuildCompositor() passScene->mIncludeOverlays = true; passScene->mFirstRQ = 254; passScene->mLastRQ = 255; - } + } Ogre::CompositorWorkspaceDef *workDef = ogreCompMgr->addWorkspaceDefinition(wsDefName); - workDef->connectExternal(0, finalNodeDefName, 0); - workDef->connect(nodeDefName, 0, finalNodeDefName, 1); + + workDef->connectExternal(0, nodeDefName, 0); + workDef->connectExternal(1, nodeDefName, 1); + + if (!this->IsRenderWindow()) + { + workDef->connect(nodeDefName, finalNodeDefName); + } + else + { + // connect the last render pass to the final compositor node + // but only input, since output goes to the render window + workDef->connect(nodeDefName, 0, finalNodeDefName, 0); + workDef->connectExternal(2, finalNodeDefName, 1); + } + } + + Ogre::CompositorChannelVec externalTargets(2u); + for (size_t i = 0u; i < 2u; ++i) + { + // Connect them in reverse order + const size_t srcIdx = 2u - i - 1u; + externalTargets[i] = this->dataPtr->ogreTexture[srcIdx]; } this->ogreCompositorWorkspace = ogreCompMgr->addWorkspace( this->scene->OgreSceneManager(), - this->RenderTarget(), + externalTargets, this->ogreCamera, this->ogreCompositorWorkspaceDefName, - true); + false); this->dataPtr->rtListener = new Ogre2RenderTargetCompositorListener(this); this->ogreCompositorWorkspace->addListener(this->dataPtr->rtListener); @@ -272,6 +298,16 @@ void Ogre2RenderTarget::DestroyCompositor() if (!this->ogreCompositorWorkspace) return; + // Restore the original order so that this->ogreTexture[1] is the one with + // FSAA (which we need for BuildCompositor to connect correctly) + const Ogre::CompositorChannelVec &externalTargets = + this->ogreCompositorWorkspace->getExternalRenderTargets(); + for (size_t i = 0u; i < 2u; ++i) + { + const size_t srcIdx = (2u - i - 1u); + this->dataPtr->ogreTexture[srcIdx] = externalTargets[i]; + } + auto engine = Ogre2RenderEngine::Instance(); auto ogreRoot = engine->OgreRoot(); Ogre::CompositorManager2 *ogreCompMgr = ogreRoot->getCompositorManager2(); @@ -443,6 +479,112 @@ void Ogre2RenderTarget::Render() // engine->OgreRoot()->getRenderSystem()->_update(); } +////////////////////////////////////////////////// +bool Ogre2RenderTarget::IsRenderWindow() const +{ + const Ogre2RenderWindow *asWindow = + dynamic_cast(this); + if (asWindow) + return true; + + return false; +} + +////////////////////////////////////////////////// +void Ogre2RenderTarget::DestroyTargetImpl() +{ + if (nullptr == this->dataPtr->ogreTexture[0]) + return; + + this->DestroyCompositor(); + + Ogre::Root *root = Ogre2RenderEngine::Instance()->OgreRoot(); + + Ogre::TextureGpuManager *textureManager = + root->getRenderSystem()->getTextureGpuManager(); + for (size_t i = 0u; i < 2u; ++i) + { + textureManager->destroyTexture(this->dataPtr->ogreTexture[i]); + this->dataPtr->ogreTexture[i] = nullptr; + } + + // TODO(anyone) there is memory leak when a render texture is destroyed. + // The RenderSystem::_cleanupDepthBuffers method used in ogre1 does not + // seem to work in ogre2 +} + +////////////////////////////////////////////////// +void Ogre2RenderTarget::BuildTargetImpl() +{ + auto engine = Ogre2RenderEngine::Instance(); + auto ogreRoot = engine->OgreRoot(); + Ogre::TextureGpuManager *textureMgr = + ogreRoot->getRenderSystem()->getTextureGpuManager(); + + for (size_t i = 0u; i < 2u; ++i) + { + this->dataPtr->ogreTexture[i] = + textureMgr->createTexture( + this->name + std::to_string(i), + Ogre::GpuPageOutStrategy::Discard, + Ogre::TextureFlags::RenderToTexture, + Ogre::TextureTypes::Type2D); + + this->dataPtr->ogreTexture[i]->setResolution(this->width, this->height); + this->dataPtr->ogreTexture[i]->setNumMipmaps(1u); + this->dataPtr->ogreTexture[i]->setPixelFormat(Ogre::PFG_RGBA8_UNORM_SRGB); + + this->dataPtr->ogreTexture[i]->scheduleTransitionTo( + Ogre::GpuResidency::Resident); + } +} + +////////////////////////////////////////////////// +unsigned int Ogre2RenderTarget::GLIdImpl() const +{ + if (!this->dataPtr->ogreTexture[0]) + return 0; + + unsigned int texId; + this->dataPtr->ogreTexture[1]->getCustomAttribute("msFinalTextureBuffer", + &texId); + + return static_cast(texId); +} + +////////////////////////////////////////////////// +uint8_t Ogre2RenderTarget::TargetFSAA() const +{ + // check if target fsaa is supported + std::vector fsaaLevels = + Ogre2RenderEngine::Instance()->FSAALevels(); + unsigned int targetFSAA = this->antiAliasing; + auto const it = std::find(fsaaLevels.begin(), fsaaLevels.end(), targetFSAA); + + if (it == fsaaLevels.end()) + { + // output warning but only do it once + static bool ogre2FSAAWarn = false; + if (ogre2FSAAWarn) + { + ignwarn << "Anti-aliasing level of '" << this->antiAliasing << "' " + << "is not supported. Setting to 0" << std::endl; + ogre2FSAAWarn = true; + } + } + + if (targetFSAA == 0u) + targetFSAA = 1u; + + return static_cast(targetFSAA); +} + +////////////////////////////////////////////////// +Ogre::TextureGpu *Ogre2RenderTarget::RenderTargetImpl() const +{ + return this->dataPtr->ogreTexture[1]; +} + ////////////////////////////////////////////////// uint32_t Ogre2RenderTarget::VisibilityMask() const { @@ -460,12 +602,15 @@ void Ogre2RenderTarget::UpdateBackgroundColor() { if (this->colorDirty) { - // set background color in compositor clear pass def + // set background color in the first pass that clears + // the RT (both node and its definition). auto nodeSeq = this->ogreCompositorWorkspace->getNodeSequence(); - auto pass = nodeSeq[0]->_getPasses()[0]->getDefinition(); - auto clearPass = dynamic_cast(pass); - const_cast(clearPass)->setAllClearColours( - this->ogreBackgroundColor); + auto pass = nodeSeq[0]->_getPasses()[0]; + pass->getRenderPassDesc()->setClearColour(this->ogreBackgroundColor); + + auto passDef = pass->getDefinition(); + const_cast(passDef)->setAllClearColours( + this->ogreBackgroundColor); this->colorDirty = false; } @@ -515,18 +660,21 @@ void Ogre2RenderTarget::UpdateRenderPassChain() this->dataPtr->kBaseNodeName, this->ogreCompositorWorkspaceDefName + "/" + this->dataPtr->kFinalNodeName, - this->renderPasses, this->renderPassDirty); + this->renderPasses, + this->renderPassDirty, + &this->dataPtr->ogreTexture, + this->IsRenderWindow()); this->renderPassDirty = false; } ////////////////////////////////////////////////// void Ogre2RenderTarget::UpdateRenderPassChain( - Ogre::CompositorWorkspace *_workspace, - const std::string &_workspaceDefName, + Ogre::CompositorWorkspace *_workspace, const std::string &_workspaceDefName, const std::string &_baseNode, const std::string &_finalNode, const std::vector &_renderPasses, - bool _recreateNodes) + bool _recreateNodes, Ogre::TextureGpu *(*_ogreTextures)[2], + bool _isRenderWindow) { if (!_workspace || _workspaceDefName.empty() || _baseNode.empty() || _finalNode.empty() || _renderPasses.empty()) @@ -550,10 +698,10 @@ void Ogre2RenderTarget::UpdateRenderPassChain( ogre2RenderPass->OgreCompositorNodeDefinitionName()); // check if we need to create all nodes or just update the connections. - // if node does not exist then it means it has not been added to the - // chain yet, in which case, we need to recreate the nodes and - // connections - if (!node) + // if node does not exist then it means it either has not been added to + // the chain yet or it was removed because it was disabled. + // In both cases, we need to recreate the nodes and connections + if (!node && ogre2RenderPass->IsEnabled()) { _recreateNodes = true; } @@ -582,7 +730,7 @@ void Ogre2RenderTarget::UpdateRenderPassChain( // the first node is the base scene pass node std::string outNodeDefName = _baseNode; // the final compositor node - std::string finalNodeDefName = _finalNode; + const std::string finalNodeDefName = _finalNode; std::string inNodeDefName; // if new nodes need to be added then clear everything, @@ -592,6 +740,8 @@ void Ogre2RenderTarget::UpdateRenderPassChain( else workspaceDef->clearAllInterNodeConnections(); + int numActiveNodes = 0; + // chain the render passes by connecting all the ogre compositor nodes // in between the base scene pass node and the final compositor node for (const auto &pass : _renderPasses) @@ -605,18 +755,43 @@ void Ogre2RenderTarget::UpdateRenderPassChain( { workspaceDef->connect(outNodeDefName, inNodeDefName); outNodeDefName = inNodeDefName; + ++numActiveNodes; } } - // connect the last render pass to the final compositor node - workspaceDef->connect(outNodeDefName, 0, finalNodeDefName, 1); + workspaceDef->connectExternal(0, _baseNode, 0); + workspaceDef->connectExternal(1, _baseNode, 1); + + if (!_isRenderWindow) + { + // connect the last render pass to the final compositor node + workspaceDef->connect(outNodeDefName, finalNodeDefName); + + // We must ensure the output is always in ogreTextures[1] + const bool bMustSwapRts = (numActiveNodes & 0x01) == 0u; + + const Ogre::CompositorChannelVec &externalTargets = + _workspace->getExternalRenderTargets(); + for (size_t i = 0u; i < 2u; ++i) + { + const size_t srcIdx = bMustSwapRts ? (2u - i - 1u) : i; + (*_ogreTextures)[srcIdx] = externalTargets[i]; + } + } + else + { + // connect the last render pass to the final compositor node + // but only input, since output goes to the render window + workspaceDef->connect(outNodeDefName, 0, finalNodeDefName, 0); + workspaceDef->connectExternal(2, _finalNode, 1); + } + // if new node definitions were added then recreate all the compositor nodes, // otherwise update the connections if (_recreateNodes) { // clearAll requires the output to be connected again. - workspaceDef->connectExternal(0, finalNodeDefName, 0); _workspace->recreateAllNodes(); } else @@ -625,14 +800,6 @@ void Ogre2RenderTarget::UpdateRenderPassChain( } } -////////////////////////////////////////////////// -void Ogre2RenderTarget::UpdateShadowNode() -{ - ignwarn << "Ogre2RenderTarget::UpdateShadowNode() is deprecated and " - "replaced by Ogre2Scene:UpdateShadowNode()" - << std::endl; -} - ////////////////////////////////////////////////// void Ogre2RenderTarget::RebuildImpl() { @@ -678,9 +845,12 @@ void Ogre2RenderTarget::RebuildMaterial() ////////////////////////////////////////////////// // Ogre2RenderTexture ////////////////////////////////////////////////// +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" Ogre2RenderTexture::Ogre2RenderTexture() { } +#pragma GCC diagnostic pop ////////////////////////////////////////////////// Ogre2RenderTexture::~Ogre2RenderTexture() @@ -693,12 +863,6 @@ void Ogre2RenderTexture::Destroy() this->DestroyTarget(); } -////////////////////////////////////////////////// -Ogre::TextureGpu *Ogre2RenderTexture::RenderTarget() const -{ - return this->ogreTexture; -} - ////////////////////////////////////////////////// void Ogre2RenderTexture::RebuildTarget() { @@ -709,66 +873,19 @@ void Ogre2RenderTexture::RebuildTarget() ////////////////////////////////////////////////// void Ogre2RenderTexture::DestroyTarget() { - if (nullptr == this->ogreTexture) - return; - - Ogre::Root *root = Ogre2RenderEngine::Instance()->OgreRoot(); - - Ogre::TextureGpuManager *textureManager = - root->getRenderSystem()->getTextureGpuManager(); - textureManager->destroyTexture(this->ogreTexture); - - this->ogreTexture = nullptr; + Ogre2RenderTarget::DestroyTargetImpl(); } ////////////////////////////////////////////////// void Ogre2RenderTexture::BuildTarget() { - // check if target fsaa is supported - std::vector fsaaLevels = - Ogre2RenderEngine::Instance()->FSAALevels(); - unsigned int targetFSAA = this->antiAliasing; - auto const it = std::find(fsaaLevels.begin(), fsaaLevels.end(), targetFSAA); - - if (it == fsaaLevels.end()) - { - // output warning but only do it once - static bool ogre2FSAAWarn = false; - if (ogre2FSAAWarn) - { - ignwarn << "Anti-aliasing level of '" << this->antiAliasing << "' " - << "is not supported. Setting to 0" << std::endl; - ogre2FSAAWarn = true; - } - } - - auto engine = Ogre2RenderEngine::Instance(); - auto ogreRoot = engine->OgreRoot(); - Ogre::TextureGpuManager *textureMgr = - ogreRoot->getRenderSystem()->getTextureGpuManager(); - this->ogreTexture = textureMgr->createOrRetrieveTexture( - this->name, - Ogre::GpuPageOutStrategy::SaveToSystemRam, - Ogre::TextureFlags::RenderToTexture, - Ogre::TextureTypes::Type2D); - - this->ogreTexture->setResolution(this->width, this->height); - this->ogreTexture->setNumMipmaps(1u); - this->ogreTexture->setPixelFormat(Ogre::PFG_RGBA8_UNORM_SRGB); - - this->ogreTexture->scheduleTransitionTo( - Ogre::GpuResidency::Resident); + Ogre2RenderTarget::BuildTargetImpl(); } ////////////////////////////////////////////////// unsigned int Ogre2RenderTexture::GLId() const { - if (!this->ogreTexture) - return 0; - - unsigned int texId; - this->ogreTexture->getCustomAttribute("msFinalTextureBuffer", &texId); - return static_cast(texId); + return Ogre2RenderTarget::GLIdImpl(); } ////////////////////////////////////////////////// @@ -783,6 +900,12 @@ void Ogre2RenderTexture::PostRender() Ogre2RenderTarget::PostRender(); } +////////////////////////////////////////////////// +Ogre::TextureGpu *Ogre2RenderTexture::RenderTarget() const +{ + return Ogre2RenderTarget::RenderTargetImpl(); +} + ////////////////////////////////////////////////// // Ogre2RenderWindow ////////////////////////////////////////////////// @@ -795,6 +918,12 @@ Ogre2RenderWindow::~Ogre2RenderWindow() { } +////////////////////////////////////////////////// +bool Ogre2RenderWindow::IsRenderWindow() const +{ + return true; +} + ////////////////////////////////////////////////// Ogre::TextureGpu *Ogre2RenderWindow::RenderTarget() const { diff --git a/ogre2/src/Ogre2RenderTargetMaterial.cc b/ogre2/src/Ogre2RenderTargetMaterial.cc index 3bf470361..d87645a78 100644 --- a/ogre2/src/Ogre2RenderTargetMaterial.cc +++ b/ogre2/src/Ogre2RenderTargetMaterial.cc @@ -77,3 +77,4 @@ Ogre::Technique *Ogre2RenderTargetMaterial::handleSchemeNotFound( } return nullptr; } + diff --git a/ogre2/src/Ogre2ThermalCamera.cc b/ogre2/src/Ogre2ThermalCamera.cc index ef37f4ab1..60ac1635c 100644 --- a/ogre2/src/Ogre2ThermalCamera.cc +++ b/ogre2/src/Ogre2ThermalCamera.cc @@ -730,17 +730,14 @@ void Ogre2ThermalCamera::CreateThermalTexture() nodeDef->setNumTargetPass(2); Ogre::CompositorTargetDef *colorTargetDef = nodeDef->addTargetPass("colorTexture"); - colorTargetDef->setNumPasses(2); + colorTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - colorTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue(0, 0, 0)); // scene pass Ogre::CompositorPassSceneDef *passScene = static_cast( colorTargetDef->addPass(Ogre::PASS_SCENE)); + passScene->setAllLoadActions(Ogre::LoadAction::Clear); + passScene->setAllClearColours(Ogre::ColourValue(0, 0, 0)); // thermal camera should not see particles passScene->mVisibilityMask = IGN_VISIBILITY_ALL & ~Ogre2ParticleEmitter::kParticleVisibilityFlags; @@ -749,17 +746,15 @@ void Ogre2ThermalCamera::CreateThermalTexture() // rt_input target - converts to thermal Ogre::CompositorTargetDef *inputTargetDef = nodeDef->addTargetPass("rt_input"); - inputTargetDef->setNumPasses(2); + inputTargetDef->setNumPasses(1); { - // clear pass - Ogre::CompositorPassClearDef *passClear = - static_cast( - inputTargetDef->addPass(Ogre::PASS_CLEAR)); - passClear->setAllClearColours(Ogre::ColourValue(this->ambient, 0, 1.0)); // quad pass Ogre::CompositorPassQuadDef *passQuad = static_cast( inputTargetDef->addPass(Ogre::PASS_QUAD)); + passQuad->setAllLoadActions(Ogre::LoadAction::Clear); + passQuad->setAllClearColours(Ogre::ColourValue(this->ambient, 0, 1.0)); + passQuad->mMaterialName = this->dataPtr->thermalMaterial->getName(); passQuad->addQuadTextureSource(0, "depthTexture"); passQuad->addQuadTextureSource(1, "colorTexture"); diff --git a/ogre2/src/media/materials/programs/depth_camera_final_fs.glsl b/ogre2/src/media/materials/programs/depth_camera_final_fs.glsl index ca3be0d0c..ef5eec270 100644 --- a/ogre2/src/media/materials/programs/depth_camera_final_fs.glsl +++ b/ogre2/src/media/materials/programs/depth_camera_final_fs.glsl @@ -31,10 +31,18 @@ uniform float far; uniform float min; uniform float max; +uniform vec4 texResolution; + void main() { float tolerance = 1e-6; - vec4 p = texture(inputTexture, inPs.uv0); + + // Note: We use texelFetch because p.a contains an uint32 and sampling + // (even w/ point filtering) causes p.a to loss information (e.g. + // values close to 0 get rounded to 0) + // + // See https://github.com/ignitionrobotics/ign-rendering/issues/332 + vec4 p = texelFetch(inputTexture, ivec2(inPs.uv0 *texResolution.xy), 0); vec3 point = p.xyz; diff --git a/ogre2/src/media/materials/scripts/depth_camera.material b/ogre2/src/media/materials/scripts/depth_camera.material index e0b87e166..602c69505 100644 --- a/ogre2/src/media/materials/scripts/depth_camera.material +++ b/ogre2/src/media/materials/scripts/depth_camera.material @@ -88,6 +88,8 @@ fragment_program DepthCameraFinalFS glsl default_params { param_named inputTexture int 0 + + param_named_auto texResolution texture_size 0 } } diff --git a/src/GaussianNoisePass_TEST.cc b/src/GaussianNoisePass_TEST.cc index aa885d0b6..61c73b6d9 100644 --- a/src/GaussianNoisePass_TEST.cc +++ b/src/GaussianNoisePass_TEST.cc @@ -85,8 +85,9 @@ void GaussianNoisePassTest::GaussianNoise(const std::string &_renderEngine) double biasStdDev = 0.007; noisePass->SetBiasStdDev(biasStdDev); // expect bias to be within 3-sigma - EXPECT_LE(std::fabs(noisePass->Bias()), biasMean + biasStdDev*3); - EXPECT_GE(std::fabs(noisePass->Bias()), biasMean - biasStdDev*3); + // Note, tol relaxed to 4-sigma to fix flaky test + EXPECT_LE(std::fabs(noisePass->Bias()), biasMean + biasStdDev*4); + EXPECT_GE(std::fabs(noisePass->Bias()), biasMean - biasStdDev*4); } ///////////////////////////////////////////////// diff --git a/src/Image.cc b/src/Image.cc index 74ed14fb6..a2ad9cc43 100644 --- a/src/Image.cc +++ b/src/Image.cc @@ -19,6 +19,16 @@ using namespace ignition; using namespace rendering; +////////////////////////////////////////////////// +template +struct ArrayDeleter +{ + void operator () (T const * p) + { + delete [] p; + } +}; + ////////////////////////////////////////////////// Image::Image(unsigned int _width, unsigned int _height, PixelFormat _format) : @@ -27,7 +37,7 @@ Image::Image(unsigned int _width, unsigned int _height, { this->format = PixelUtil::Sanitize(_format); unsigned int size = this->MemorySize(); - this->data = DataPtr(new unsigned char[size]); + this->data = DataPtr(new unsigned char[size], ArrayDeleter()); } ////////////////////////////////////////////////// diff --git a/test/integration/depth_camera.cc b/test/integration/depth_camera.cc index c014d5985..211c5ba72 100644 --- a/test/integration/depth_camera.cc +++ b/test/integration/depth_camera.cc @@ -254,6 +254,8 @@ void DepthCameraTest::DepthCameraBoxes( unsigned int ma = *mrgba >> 0 & 0xFF; EXPECT_EQ(0u, mr); EXPECT_EQ(0u, mg); + // Note: If it fails here, it may be this problem again: + // https://github.com/ignitionrobotics/ign-rendering/issues/332 EXPECT_GT(mb, 0u); // Far left and right points should be red (background color) @@ -453,6 +455,8 @@ void DepthCameraTest::DepthCameraBoxes( unsigned int a = *rgba >> 0 & 0xFF; EXPECT_EQ(0u, r); EXPECT_EQ(0u, g); + // Note: If it fails here, it may be this problem again: + // https://github.com/ignitionrobotics/ign-rendering/issues/332 EXPECT_GT(b, 0u); EXPECT_EQ(255u, a); } @@ -762,20 +766,12 @@ void DepthCameraTest::DepthCameraParticles( ignition::rendering::unloadEngine(engine->Name()); } -#ifdef __APPLE__ -TEST_P(DepthCameraTest, DISABLED_DepthCameraBoxes) -#else TEST_P(DepthCameraTest, DepthCameraBoxes) -#endif { DepthCameraBoxes(GetParam()); } -#ifdef __APPLE__ -TEST_P(DepthCameraTest, DISABLED_DepthCameraParticles) -#else TEST_P(DepthCameraTest, DepthCameraParticles) -#endif { DepthCameraParticles(GetParam()); } diff --git a/tutorials/04_lightmap.md b/tutorials/04_lightmap.md index fa20e4a67..e298cd4ee 100644 --- a/tutorials/04_lightmap.md +++ b/tutorials/04_lightmap.md @@ -26,7 +26,7 @@ To import a model go to `file` > `import` > (choose file type of model) The next step is to create UV unwraps for your model. In Blender usually the first UVmap for any mesh created is for PBR textures and the second UV channel is for light map information. This secondary UV set is different from the UVs used to map things like color maps or normal maps because each polygon surface is receiving different lighting from all other polygons so each UV coordinate also needs to be unique. -For a fast automatic light map unwrap hit F3 and then search for “Lightmap pack” +For a fast automatic light map unwrap hit F3 and then search for “Lightmap pack”. Make sure to the “margin” is set to 1 for max distance between faces (reduces shadow bleeding during bake) @image html img/lightmap_unwrap.png @@ -41,9 +41,9 @@ If the texture is black use the color picker and select the UV island and it sho @image html img/lightmap_new_texture.png -### Step 4: Material shader +### Step 4: Material setup / Baking -In order to have the texture show we must assign it to a new material. With your model selected open the Shader tab and select `Add` > `Image` > `Image texture`. This will create a new image texture node. +In order to have the texture show the baked lighting we must assign it to a new material. With your model selected open the Shader tab and select `Add` > `Image` > `Image texture`. This will create a new image texture node. * Add an image texture * Connect the color from the image node to the base color of the material color (principled BSDF) @@ -52,16 +52,21 @@ After this step add another image texture in the shader editor @image html img/lightmap_material_shader.png -### Step 5: Render/Bake - -When baking we have the option to either bake in all the lighting, both indirect and direct, or just the indirect lighting, also known as global illumination or bounced lighting, and use realtime lights in Ignition for the direct lighting and shadows. With the latter method we get sharper lighting and shadow detail as well as more accurate lighting on our dynamic objects however performance will be impacted by having more real time lights. Even with all the lighting baked it’s still a good idea to have one realtime light such as a large point light or directional light works particularly well in order to enhance the effects of the physically based materials. +@image html img/lightmap_bake.jpg Now that the material node is set up, open the render properties and make sure that the cycles renderer active in the render engines drop down. -Scroll down to where it says bake, and then with the second image node selected (MeshLightBake step 4) hit bake. Blender should now start baking the light map for the model. +Scroll down to where it says bake, and then with the second image node selected (MeshLightBake) hit bake. Blender should now start baking the light map for the model. -@image html img/lightmap_new_texture.png +### Step 5: Show Baked Lighting + +After the bake is finished you should have a separate light map of your model. +* To see it applied real time in your scene, go back to the hypershade / materials editor. Click on the Image texture that has the baked light map information and connect it to the base color. + +@image html img/lightmap_material_connection.jpg -The above image shows the model with baked in lighting (Blender Viewport Shading turned on). +Model with baked in lighting (Blender Viewport Shading turned on): + +@image html img/lightmap_rendered.png ### Step 6: Test/Export @@ -69,12 +74,16 @@ Light maps are great for working with bigger scenes. With baked-in lighting, com To export light map image go to `UV editor` > `Image` > `Save as` -Lightmap baking applied to a more complex scene: +@image html img/lightmap_depot_saved.jpg + +Light map baking applied to a more complex scene: @image html img/lightmap_depot_render.png ## Using the light map in Ignition +When baking we have the option to either bake in all the lighting, both indirect and direct, or just the indirect lighting, also known as global illumination or bounced lighting, and use real time lights in Ignition for the direct lighting and shadows. With the latter method we get sharper lighting and shadow detail as well as more accurate lighting on our dynamic objects however performance will be impacted by having more real time lights. Even with all the lighting baked it’s still a good idea to have one real time light such as a large point light or directional light works particularly well in order to enhance the effects of the physically based materials. + Lightmaps can be applied to a mesh in Ignition the same way as other texture maps. Create an `ignition::rendering::Material` and specify a light map texture by calling [SetLightMap](https://ignitionrobotics.org/api/rendering/5.0/classignition_1_1rendering_1_1Material.html#addc6eb6206e0a17ab82aeaea543e8c71). Recall that when creating the light map UV texture in Step 2, we typically use a secondary UV set for light maps. Make sure to specify the index of the light map UV set as the second argument to this function. diff --git a/tutorials/img/lightmap_bake.jpg b/tutorials/img/lightmap_bake.jpg new file mode 100644 index 000000000..84993a8d6 Binary files /dev/null and b/tutorials/img/lightmap_bake.jpg differ diff --git a/tutorials/img/lightmap_depot_saved.jpg b/tutorials/img/lightmap_depot_saved.jpg new file mode 100644 index 000000000..dc71dd95c Binary files /dev/null and b/tutorials/img/lightmap_depot_saved.jpg differ diff --git a/tutorials/img/lightmap_material_connection.jpg b/tutorials/img/lightmap_material_connection.jpg new file mode 100644 index 000000000..f075e3ab6 Binary files /dev/null and b/tutorials/img/lightmap_material_connection.jpg differ diff --git a/tutorials/img/lightmap_rendered.png b/tutorials/img/lightmap_rendered.png new file mode 100644 index 000000000..4825b52d2 Binary files /dev/null and b/tutorials/img/lightmap_rendered.png differ