Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix material switching for objects using shaders (ogre2) #533

Merged
merged 11 commits into from
Jan 18, 2022
6 changes: 4 additions & 2 deletions ogre2/src/Ogre2Material.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
43 changes: 33 additions & 10 deletions ogre2/src/Ogre2MaterialSwitcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ogre2MaterialSwitcher *,
std::map<Ogre::SubItem *, Ogre::MaterialPtr>> materialMap;

/////////////////////////////////////////////////
Ogre2MaterialSwitcher::Ogre2MaterialSwitcher(Ogre2ScenePtr _scene)
{
Expand Down Expand Up @@ -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())
Expand All @@ -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();
}
Expand All @@ -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();
}

/////////////////////////////////////////////////
Expand Down
174 changes: 174 additions & 0 deletions test/integration/camera.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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");
};

/////////////////////////////////////////////////
Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<< _renderEngine << std::endl;
<< _renderEngine << std::endl;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
igndbg << "Engine '" << _renderEngine
<< "' is not supported" << std::endl;
igndbg << "Engine '" << _renderEngine
<< "' is not supported" << std::endl;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>();
unsigned char *data2 = image2.Data<unsigned char>();
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<int>(data[mid]);
int g = static_cast<int>(data[mid+1]);
int b = static_cast<int>(data[mid+2]);
int r2 = static_cast<int>(data2[mid]);
int g2 = static_cast<int>(data2[mid+1]);
int b2 = static_cast<int>(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)
{
Expand All @@ -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());
Expand Down
25 changes: 25 additions & 0 deletions test/media/materials/programs/simple_color_330_fs.glsl
Original file line number Diff line number Diff line change
@@ -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);
}
31 changes: 31 additions & 0 deletions test/media/materials/programs/simple_color_330_vs.glsl
Original file line number Diff line number Diff line change
@@ -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;
}
21 changes: 21 additions & 0 deletions test/media/materials/programs/simple_color_fs.glsl
Original file line number Diff line number Diff line change
@@ -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);
}
Loading