diff --git a/ogre2/src/Ogre2Material.cc b/ogre2/src/Ogre2Material.cc index 5ef45c6a1..acd744a5b 100644 --- a/ogre2/src/Ogre2Material.cc +++ b/ogre2/src/Ogre2Material.cc @@ -1007,12 +1007,13 @@ void Ogre2Material::SetVertexShader(const std::string &_path) return; } + std::string baseName = common::basename(_path); Ogre::ResourceGroupManager::getSingleton().addResourceLocation(_path, "FileSystem", "General", false); Ogre::HighLevelGpuProgramPtr vertexShader = Ogre::HighLevelGpuProgramManager::getSingletonPtr()->createProgram( - "_ign_" + _path, + "_ign_" + baseName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "glsl", Ogre::GpuProgramType::GPT_VERTEX_PROGRAM); @@ -1064,9 +1065,10 @@ void Ogre2Material::SetFragmentShader(const std::string &_path) Ogre::ResourceGroupManager::getSingleton().addResourceLocation(_path, "FileSystem", "General", false); + std::string baseName = common::basename(_path); Ogre::HighLevelGpuProgramPtr fragmentShader = Ogre::HighLevelGpuProgramManager::getSingleton().createProgram( - "_ign_" + _path, + "_ign_" + baseName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "glsl", Ogre::GpuProgramType::GPT_FRAGMENT_PROGRAM); diff --git a/ogre2/src/Ogre2MaterialSwitcher.cc b/ogre2/src/Ogre2MaterialSwitcher.cc index d1ae6dee4..45c38e88b 100644 --- a/ogre2/src/Ogre2MaterialSwitcher.cc +++ b/ogre2/src/Ogre2MaterialSwitcher.cc @@ -36,6 +36,12 @@ using namespace ignition; using namespace rendering; +/// \brief A map of ogre sub item pointer to their original low level material +/// \todo(anyone) Added here for ABI compatibity This can be removed once +/// ign-rendering7 switches to Hlms customization for "switching" materials +std::map> materialMap; + ///////////////////////////////////////////////// Ogre2MaterialSwitcher::Ogre2MaterialSwitcher(Ogre2ScenePtr _scene) { @@ -80,7 +86,6 @@ void Ogre2MaterialSwitcher::cameraPreRenderScene( // swap item to use v1 shader material // Note: keep an eye out for performance impact on switching materials // on the fly. We are not doing this often so should be ok. - this->datablockMap.clear(); auto itor = this->scene->OgreSceneManager()->getMovableObjectIterator( Ogre::ItemFactory::FACTORY_TYPE_NAME); while (itor.hasMoreElements()) @@ -95,20 +100,30 @@ void Ogre2MaterialSwitcher::cameraPreRenderScene( for (unsigned int i = 0; i < item->getNumSubItems(); ++i) { Ogre::SubItem *subItem = item->getSubItem(i); - Ogre::HlmsDatablock *datablock = subItem->getDatablock(); - this->datablockMap[subItem] = datablock; - subItem->setCustomParameter(1, Ogre::Vector4(this->currentColor.R(), this->currentColor.G(), this->currentColor.B(), 1.0)); - // check if it's an overlay material by assuming the - // depth check and depth write properties are off. - if (!datablock->getMacroblock()->mDepthWrite && - !datablock->getMacroblock()->mDepthCheck) - subItem->setMaterial(this->plainOverlayMaterial); - else + // case when item is using low level materials + // e.g. shaders + if (!subItem->getMaterial().isNull()) + { + materialMap[this][subItem] = subItem->getMaterial(); subItem->setMaterial(this->plainMaterial); + } + // regular Pbs Hlms datablock + else + { + Ogre::HlmsDatablock *datablock = subItem->getDatablock(); + this->datablockMap[subItem] = datablock; + // check if it's an overlay material by assuming the + // depth check and depth write properties are off. + if (!datablock->getMacroblock()->mDepthWrite && + !datablock->getMacroblock()->mDepthCheck) + subItem->setMaterial(this->plainOverlayMaterial); + else + subItem->setMaterial(this->plainMaterial); + } } itor.moveNext(); } @@ -131,9 +146,17 @@ void Ogre2MaterialSwitcher::cameraPostRenderScene( auto it = this->datablockMap.find(subItem); if (it != this->datablockMap.end()) subItem->setDatablock(it->second); + else + { + auto mIt = materialMap[this].find(subItem); + if (mIt != materialMap[this].end()) + subItem->setMaterial(mIt->second); + } } itor.moveNext(); } + this->datablockMap.clear(); + materialMap[this].clear(); } ///////////////////////////////////////////////// diff --git a/test/integration/camera.cc b/test/integration/camera.cc index 95619639d..c228cdd46 100644 --- a/test/integration/camera.cc +++ b/test/integration/camera.cc @@ -25,6 +25,7 @@ #include "ignition/rendering/RenderEngine.hh" #include "ignition/rendering/RenderingIface.hh" #include "ignition/rendering/Scene.hh" +#include "ignition/rendering/ShaderParams.hh" using namespace ignition; using namespace rendering; @@ -49,6 +50,14 @@ class CameraTest: public testing::Test, // Test and verify camera select function method using Selection Buffer public: void VisualAt(const std::string &_renderEngine); + + // Test selecting visual with custom shader + public: void ShaderSelection(const std::string &_renderEngine); + + // Path to test media directory + public: const std::string TEST_MEDIA_PATH = + ignition::common::joinPaths(std::string(PROJECT_SOURCE_PATH), + "test", "media"); }; ///////////////////////////////////////////////// @@ -552,6 +561,165 @@ void CameraTest::Visibility(const std::string &_renderEngine) rendering::unloadEngine(engine->Name()); } +///////////////////////////////////////////////// +void CameraTest::ShaderSelection(const std::string &_renderEngine) +{ + if (_renderEngine == "optix") + { + igndbg << "Custom shaders are not supported yet in rendering engine: " + << _renderEngine << std::endl; + return; + } + else if (_renderEngine == "ogre2") + { + // \todo(anyone) test fails on github action (Bionic) but pass on other + // builds. Need to investigate further. + // Github action sets the MESA_GL_VERSION_OVERRIDE variable + // so check for this variable and disable test if it is set. +#ifdef __linux__ + std::string value; + bool result = common::env("MESA_GL_VERSION_OVERRIDE", value, true); + if (result && value == "3.3") + { + igndbg << "Test is run on machine with software rendering or mesa driver " + << "Skipping test. " << std::endl; + return; + } +#endif + } + + // This test checks that custom shaders are being rendering correctly in + // camera view. It also verifies that visual selection is working and the + // visual's material remains the same after selection. + + // create and populate scene + RenderEngine *engine = rendering::engine(_renderEngine); + if (!engine) + { + igndbg << "Engine '" << _renderEngine + << "' is not supported" << std::endl; + return; + } + + ScenePtr scene = engine->CreateScene("scene"); + ASSERT_TRUE(scene != nullptr); + scene->SetAmbientLight(1, 1, 1); + + VisualPtr root = scene->RootVisual(); + + // create directional light + DirectionalLightPtr light = scene->CreateDirectionalLight(); + light->SetDirection(1.0, 0.0, -1); + light->SetDiffuseColor(0.5, 0.5, 0.5); + light->SetSpecularColor(0.5, 0.5, 0.5); + root->AddChild(light); + + std::string vertexShaderFile; + std::string fragmentShaderFile; + if (_renderEngine == "ogre2") + { + vertexShaderFile = "simple_color_330_vs.glsl"; + fragmentShaderFile = "simple_color_330_fs.glsl"; + } + else if (_renderEngine == "ogre") + { + vertexShaderFile = "simple_color_vs.glsl"; + fragmentShaderFile = "simple_color_fs.glsl"; + } + + // create shader materials + // path to look for vertex and fragment shader parameters + std::string vertexShaderPath = ignition::common::joinPaths( + TEST_MEDIA_PATH, "materials", "programs", vertexShaderFile); + std::string fragmentShaderPath = ignition::common::joinPaths( + TEST_MEDIA_PATH, "materials", "programs", fragmentShaderFile); + + // create shader material + ignition::rendering::MaterialPtr shader = scene->CreateMaterial(); + shader->SetVertexShader(vertexShaderPath); + shader->SetFragmentShader(fragmentShaderPath); + + // create visual + VisualPtr visual = scene->CreateVisual("box"); + visual->AddGeometry(scene->CreateBox()); + visual->SetWorldPosition(2.0, 0.0, 0.0); + visual->SetWorldRotation(0.0, 0.0, 0.0); + visual->SetMaterial(shader); + root->AddChild(visual); + + // visual will clone and create a unique material + // so destroy this one + scene->DestroyMaterial(shader); + + // create camera + CameraPtr camera = scene->CreateCamera("camera"); + ASSERT_TRUE(camera != nullptr); + camera->SetLocalPosition(0.0, 0.0, 0.0); + camera->SetLocalRotation(0.0, 0.0, 0.0); + camera->SetImageWidth(800); + camera->SetImageHeight(600); + camera->SetAntiAliasing(2); + camera->SetAspectRatio(1.333); + camera->SetHFOV(IGN_PI / 2); + root->AddChild(camera); + + if (_renderEngine == "ogre2") + { + // worldviewproj_matrix is a constant defined by ogre. + // Here we add a line to add this constant to the params. + // The specified value is ignored as it will be auto bound to the + // correct type and value. + auto params = visual->Material()->VertexShaderParams(); + (*params)["worldviewproj_matrix"] = 1; + } + + // render a few frames + for (auto i = 0; i < 30; ++i) + { + camera->Update(); + } + + // capture a frame + Image image = camera->CreateImage(); + camera->Capture(image); + + // verify correct visual is returned + VisualPtr vis = camera->VisualAt( + math::Vector2i(camera->ImageWidth() / 2, camera->ImageHeight() / 2)); + EXPECT_NE(nullptr, vis); + EXPECT_EQ("box", vis->Name()); + + // capture another frame + Image image2 = camera->CreateImage(); + camera->Capture(image2); + + unsigned char *data = image.Data(); + unsigned char *data2 = image2.Data(); + unsigned int height = camera->ImageHeight(); + unsigned int width = camera->ImageWidth(); + + // verify that camera sees red color before and after selection + int mid = (height / 2 * width * 3u) + (width / 2 - 1) * 3u; + int r = static_cast(data[mid]); + int g = static_cast(data[mid+1]); + int b = static_cast(data[mid+2]); + int r2 = static_cast(data2[mid]); + int g2 = static_cast(data2[mid+1]); + int b2 = static_cast(data2[mid+2]); + + EXPECT_EQ(r, r2); + EXPECT_EQ(g, g2); + EXPECT_EQ(b, b2); + + EXPECT_GT(r, g); + EXPECT_GT(r, b); + EXPECT_EQ(g, b); + + // Clean up + engine->DestroyScene(scene); + rendering::unloadEngine(engine->Name()); +} + ///////////////////////////////////////////////// TEST_P(CameraTest, Track) { @@ -576,6 +744,12 @@ TEST_P(CameraTest, VisualAt) VisualAt(GetParam()); } +///////////////////////////////////////////////// +TEST_P(CameraTest, ShaderSelection) +{ + ShaderSelection(GetParam()); +} + INSTANTIATE_TEST_CASE_P(Camera, CameraTest, RENDER_ENGINE_VALUES, ignition::rendering::PrintToStringParam()); diff --git a/test/media/materials/programs/simple_color_330_fs.glsl b/test/media/materials/programs/simple_color_330_fs.glsl new file mode 100644 index 000000000..d8b83574b --- /dev/null +++ b/test/media/materials/programs/simple_color_330_fs.glsl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#version 330 + +out vec4 fragColor; + +void main() +{ + fragColor = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/test/media/materials/programs/simple_color_330_vs.glsl b/test/media/materials/programs/simple_color_330_vs.glsl new file mode 100644 index 000000000..8cd2eaa6d --- /dev/null +++ b/test/media/materials/programs/simple_color_330_vs.glsl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#version 330 + +in vec4 vertex; +uniform mat4 worldviewproj_matrix; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = worldviewproj_matrix * vertex; +} diff --git a/test/media/materials/programs/simple_color_fs.glsl b/test/media/materials/programs/simple_color_fs.glsl new file mode 100644 index 000000000..12d3d474c --- /dev/null +++ b/test/media/materials/programs/simple_color_fs.glsl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +void main() +{ + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/test/media/materials/programs/simple_color_vs.glsl b/test/media/materials/programs/simple_color_vs.glsl new file mode 100644 index 000000000..30c9bd640 --- /dev/null +++ b/test/media/materials/programs/simple_color_vs.glsl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +void main() +{ + gl_Position = ftransform(); +}