From 22b2bbfbe06ac68558d77948d339fbc294876c85 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Thu, 5 Oct 2023 00:58:31 -0700 Subject: [PATCH] Support skybox in wide angle cam view (#901) Signed-off-by: Ian Chen --- .../rendering/ogre2/Ogre2WideAngleCamera.hh | 10 ++ ogre2/src/Ogre2Scene.cc | 9 ++ ogre2/src/Ogre2WideAngleCamera.cc | 88 ++++++++++++++++- test/integration/sky.cc | 97 +++++++++++++++++++ 4 files changed, 203 insertions(+), 1 deletion(-) diff --git a/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh b/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh index 688f07540..2979ddec7 100644 --- a/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh +++ b/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh @@ -110,6 +110,13 @@ namespace gz std::function _subscriber) override; + /// \brief Set the background material of this camera + /// \param[in] _material Material to set the background to + public: void SetBackgroundMaterial(MaterialPtr _material); + + /// \brief Get the background material of this camera + /// \return background material + public: MaterialPtr BackgroundMaterial() const; /// \brief Returns the workspace name for the final pass /// that stitches all faces. @@ -179,6 +186,9 @@ namespace gz protected: void SetupMSAA(Ogre::CompositorManager2 *_ogreCompMgr, uint8_t _msaa); + /// \brief Update the background material + private: void UpdateBackgroundMaterial(); + /// \brief Saves the CompositorPassSceneDef of each of the 6 passes /// defined in WideAngleCamera.compositor data file for later /// manipulation. diff --git a/ogre2/src/Ogre2Scene.cc b/ogre2/src/Ogre2Scene.cc index 3bdb74b59..17b870cc2 100644 --- a/ogre2/src/Ogre2Scene.cc +++ b/ogre2/src/Ogre2Scene.cc @@ -1096,6 +1096,8 @@ WideAngleCameraPtr Ogre2Scene::CreateWideAngleCameraImpl(const unsigned int _id, { Ogre2WideAngleCameraPtr camera(new Ogre2WideAngleCamera); bool result = this->InitObject(camera, _id, _name); + if (this->backgroundMaterial) + camera->SetBackgroundMaterial(this->backgroundMaterial); return (result) ? camera : nullptr; } @@ -1535,6 +1537,13 @@ void Ogre2Scene::SetSkyEnabled(bool _enabled) { camera->SetBackgroundMaterial(skyboxMat); } + else + { + auto wideAngleCamera = + std::dynamic_pointer_cast(sensor); + if (wideAngleCamera) + wideAngleCamera->SetBackgroundMaterial(skyboxMat); + } } this->dataPtr->skyEnabled = _enabled; } diff --git a/ogre2/src/Ogre2WideAngleCamera.cc b/ogre2/src/Ogre2WideAngleCamera.cc index f0b9015ea..398ec4434 100644 --- a/ogre2/src/Ogre2WideAngleCamera.cc +++ b/ogre2/src/Ogre2WideAngleCamera.cc @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -150,6 +151,16 @@ class gz::rendering::Ogre2WideAngleCamera::Implementation /// \brief See Ogre2WideAngleCameraWorkspaceListenerPrivate public: Ogre2WideAngleCameraWorkspaceListenerPrivate workspaceListener; + /// \brief Name of sky box material + public: const std::string kSkyboxMaterialName = "SkyBox"; + + /// \brief Background material of the render target + public: MaterialPtr backgroundMaterial; + + /// \brief Flag to indicate if the render target background material has + /// changed + public: bool backgroundMaterialDirty = false; + explicit Implementation(gz::rendering::Ogre2WideAngleCamera &_owner) : workspaceListener(_owner) { @@ -216,6 +227,13 @@ void Ogre2WideAngleCamera::PreRender() { BaseCamera::PreRender(); + if (this->dataPtr->backgroundMaterialDirty) + { + this->UpdateBackgroundMaterial(); + this->RemoveAllRenderPasses(); + this->DestroyTextures(); + } + { auto thisAsCameraPtr = std::dynamic_pointer_cast(this->shared_from_this()); @@ -621,6 +639,26 @@ void Ogre2WideAngleCamera::CreateWorkspaceDefinition(bool _withMsaa) const IdString cubemapPassNodeName = _withMsaa ? "WideAngleCameraCubemapPassMsaa" : "WideAngleCameraCubemapPass"; + bool validBackground = this->dataPtr->backgroundMaterial && + !this->dataPtr->backgroundMaterial->EnvironmentMap().empty(); + + // render background, e.g. sky, after opaque stuff + if (validBackground) + { + Ogre::CompositorNodeDef *nodeDef = ogreCompMgr->getNodeDefinitionNonConst( + cubemapPassNodeName); + Ogre::CompositorTargetDef *target0 = nodeDef->getTargetPass(0); + + // quad pass + Ogre::CompositorPassQuadDef *passQuad = + static_cast( + target0->addPass(Ogre::PASS_QUAD)); + passQuad->mMaterialName = this->dataPtr->kSkyboxMaterialName + "_" + + this->Name(); + passQuad->mFrustumCorners = + Ogre::CompositorPassQuadDef::CAMERA_DIRECTION; + } + for (uint32_t faceIdx = 0u; faceIdx < kWideAngleNumCubemapFaces; ++faceIdx) { const std::string wsDefName = this->WorkspaceDefinitionName(faceIdx); @@ -969,7 +1007,7 @@ void Ogre2WideAngleCamera::CreateWideAngleTexture() if (msaa > 1u) { - SetupMSAA(ogreCompMgr, msaa); + this->SetupMSAA(ogreCompMgr, msaa); } this->CreateFacesWorkspaces(msaa > 1u); @@ -1366,3 +1404,51 @@ void Ogre2WideAngleCameraWorkspaceListenerPrivate::passPreExecute( this->owner.PrepareForFinalPass(pass); } } + +////////////////////////////////////////////////// +void Ogre2WideAngleCamera::SetBackgroundMaterial(MaterialPtr _material) +{ + this->dataPtr->backgroundMaterial = _material; + this->dataPtr->backgroundMaterialDirty = true; +} + +////////////////////////////////////////////////// +MaterialPtr Ogre2WideAngleCamera::BackgroundMaterial() const +{ + return this->dataPtr->backgroundMaterial; +} + +////////////////////////////////////////////////// +void Ogre2WideAngleCamera::UpdateBackgroundMaterial() +{ + if (!this->dataPtr->backgroundMaterialDirty) + return; + + bool validBackground = this->dataPtr->backgroundMaterial && + !this->dataPtr->backgroundMaterial->EnvironmentMap().empty(); + + if (validBackground) + { + Ogre::MaterialManager &matManager = Ogre::MaterialManager::getSingleton(); + std::string skyMatName = this->dataPtr->kSkyboxMaterialName + "_" + + this->Name(); + auto mat = matManager.getByName(skyMatName); + if (!mat) + { + auto skyboxMat = matManager.getByName(this->dataPtr->kSkyboxMaterialName); + if (!skyboxMat) + { + gzerr << "Unable to find skybox material" << std::endl; + return; + } + mat = skyboxMat->clone(skyMatName); + } + Ogre::TextureUnitState *texUnit = + mat->getTechnique(0u)->getPass(0u)->getTextureUnitState(0u); + texUnit->setTextureName(this->dataPtr->backgroundMaterial->EnvironmentMap(), + Ogre::TextureTypes::TypeCube); + texUnit->setHardwareGammaEnabled(false); + } + + this->dataPtr->backgroundMaterialDirty = false; +} diff --git a/test/integration/sky.cc b/test/integration/sky.cc index 0462e74f7..153e8150a 100644 --- a/test/integration/sky.cc +++ b/test/integration/sky.cc @@ -25,6 +25,9 @@ #include "gz/rendering/Image.hh" #include "gz/rendering/PixelFormat.hh" #include "gz/rendering/Scene.hh" +#include "gz/rendering/WideAngleCamera.hh" + +#include using namespace gz; using namespace rendering; @@ -86,6 +89,100 @@ TEST_F(SkyTest, Sky) unsigned int gSkySum = 0u; unsigned int bSkySum = 0u; + for (unsigned int i = 0; i < height; ++i) + { + for (unsigned int j = 0; j < step; j += channelCount) + { + unsigned int idx = i * step + j; + rSum += data[idx]; + gSum += data[idx + 1]; + bSum += data[idx + 2]; + + rSkySum += dataSky[idx]; + gSkySum += dataSky[idx + 1]; + bSkySum += dataSky[idx + 2]; + } + } + + // sky disabled - red background + EXPECT_GT(rSum, 0u); + EXPECT_EQ(0u, gSum); + EXPECT_EQ(0u, bSum); + + // sky enabled - blue should be the dominant color + EXPECT_GT(rSkySum, 0u); + EXPECT_GT(gSkySum, 0u); + EXPECT_GT(bSkySum, 0u); + EXPECT_GT(bSkySum, gSkySum); + EXPECT_GT(bSkySum, rSkySum); + + // Clean up + engine->DestroyScene(scene); +} + +///////////////////////////////////////////////// +TEST_F(SkyTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(WideAngleCamera)) +{ + CHECK_SUPPORTED_ENGINE("ogre2"); + + // add resources in build dir + engine->AddResourcePath( + common::joinPaths(std::string(PROJECT_BUILD_PATH), "src")); + + ScenePtr scene = engine->CreateScene("scene"); + ASSERT_NE(nullptr, scene); + scene->SetAmbientLight(0.3, 0.3, 0.3); + + scene->SetBackgroundColor(1.0, 0.0, 0.0); + + VisualPtr root = scene->RootVisual(); + ASSERT_NE(nullptr, root); + + // create camera + auto camera = scene->CreateWideAngleCamera("WideAngleCamera"); + ASSERT_NE(nullptr, camera); + + CameraLens lens; + lens.SetCustomMappingFunction(1.05, 4.0, AFT_TAN, 1.0, 0.0); + lens.SetType(MFT_CUSTOM); + lens.SetCutOffAngle(GZ_PI); + + camera->SetLens(lens); + camera->SetHFOV(2.6); + camera->SetImageWidth(100); + camera->SetImageHeight(100); + camera->SetAspectRatio(1.333); + camera->SetLocalPosition(0.0, 0.0, 0.0); + + // look up into the sky + camera->SetLocalRotation(math::Quaterniond(0, -GZ_PI/2.0, 0)); + root->AddChild(camera); + + // capture original image with red background + Image image = camera->CreateImage(); + camera->Capture(image); + + // Enable sky + scene->SetSkyEnabled(true); + + // capture image with sky enabled + Image imageSky = camera->CreateImage(); + camera->Capture(imageSky); + + // Compare image pixels + unsigned char *data = image.Data(); + unsigned char *dataSky = imageSky.Data(); + unsigned int height = camera->ImageHeight(); + unsigned int width = camera->ImageWidth(); + unsigned int channelCount = PixelUtil::ChannelCount(camera->ImageFormat()); + unsigned int step = width * channelCount; + + unsigned int rSum = 0u; + unsigned int gSum = 0u; + unsigned int bSum = 0u; + unsigned int rSkySum = 0u; + unsigned int gSkySum = 0u; + unsigned int bSkySum = 0u; for (unsigned int i = 0; i < height; ++i) {