目录
1. 用法及差异点说明
2.利用NodeMask隐藏节点
2. Switch节点
3. NodeMask和Switch混用
1. 用法及差异点说明
在osg中使Node隐藏方式有两种,一种是设置NodeMask,另外一种是使用osg的Switch类来控制。两者的区别:前者只是看不到,数据还在场景中,隐藏节点并不能影响渲染性能,不会降低内存消耗;后者是从内存中暂时移除节点,可以降低内存消耗,会提高渲染性能,需要显示时再加载进场景。对于大型复杂且暂时不需要显示的节点,采用osg::Switch将节点移出不显示该节点,可以大大提供渲染性能,降低系统内存消耗。
2.利用NodeMask隐藏节点
如下代码:
// showOrHideNode.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<osgViewer/Viewer>
#include<osg/ArgumentParser>
#include<osgDB/readFile>
#include<osg/Group>
#include<osg/Switch>
#include<osg/PositionAttitudeTransform>
using namespace osg;
int main()
{
auto spCowNode = osgDB::readRefNodeFile("cow.osg");
if (nullptr == spCowNode)
{
osg::notify(osg::FATAL) << "cow node is null\r\n";
return 1;
}
auto pGroup = new osg::Group();
pGroup->addChild(spCowNode);
osgViewer::Viewer viewer;
auto pCamera = viewer.getCamera();
auto defaultCullMask = pCamera->getCullMask(); // 相机默认的裁剪掩码
auto defaultCowNodeMask = spCowNode->getNodeMask(); // 奶牛节点默认的掩码
std::cout.setf(std::cout.hex, std::cout.basefield); // 以16进制输出掩码值
std::cout.setf(std::cout.showbase); // 显示16进制前面的ox
std::cout << "default Cull Mask of camera is : " << defaultCullMask << std::endl;
std::cout << "default Mask of cow node is : " << defaultCowNodeMask << std::endl;
// pCamera->setCullMask(0x0); // 这里将相机裁剪掩码设置为0,即所有节点都被裁剪,不被渲染,此时视景器中的场景是空的,任何场景都不会绘制
spCowNode->setNodeMask(0x0); // 更改奶牛的节点掩码值为0,则奶牛不会被渲染即不会在视景器中绘制
viewer.setSceneData(pGroup);
return viewer.run();
}
控制台输出如下:
视景器中没有任何场景节点,即是空的,因为是空的,就不贴图了。
说明:
- 如果取消上面代码第32行注释,即将相机裁剪掩码设置为0,则场景中所有节点都被裁剪掉,不会被渲染,此时视景器中的场景是空的,任何节点都不会绘制。
- 因为第33行设置奶牛的节点掩码为0,则奶牛不会被渲染,此时视景器中的场景是空的。
- 默认情况下,节点掩码和相机裁剪掩码都为 0xffffffff,即int类型的最大值。
osg幕后实现的原理:
节点掩码是一系列的位集合,默认值是0xffffffff,即所有的位都被设置。节点掩码最常用的作用是用在遍历场景树中。例如:当利用osg::NodeVisitor遍历场景树时,将遍历掩码(TraversalMask)和节点掩码(NodeMask)进行位与(&)操作,从而决定该节点是否被处理或遍历。例如:一个节点通过如下代码设置其掩码为0x02:
spNode->setNodeMask(0x02);
而通过如下代码:
osgViewer::Viewer viewer;
auto pCamera = viewer.getCamera();
pCamera->setCullMask(0x04);
将相机的裁剪掩码设置为0x04。当利用osg::NodeVisitor遍历场景树时,会将相机的裁剪掩码即这里的0x04作为遍历掩码(TraversalMask)然后再&上节点掩码(NodeMask)0x02,如下:
TraversalMask & NodeMask = 0x04 & 0x02 = 0100 & 0010 = 0000
结果为0,则该节点及其下面的子节点被相机忽略即被裁剪掉,因此不会被渲染绘制。相反地,将相机的裁剪掩码设置为0x03,则:
TraversalMask & NodeMask = 0x03 & 0x02 = 0011 & 0010 = 0010
结果为二进制10即十进制2,不为0,则该节点及其下面的子节点不会被相机裁剪,因此会被渲染绘制。
总结:
节点是否会被渲染或裁剪,取决于相机裁剪掩码和节点掩码&之后的值,该值如果为1,则该节点会被渲染;为0,则会被相机裁剪,不会被渲染。
2. Switch节点
如下代码:
// showOrHideNode.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<osgViewer/Viewer>
#include<osg/ArgumentParser>
#include<osgDB/readFile>
#include<osg/Group>
#include<osg/Switch>
#include<osg/PositionAttitudeTransform>
using namespace osg;
int main()
{
auto spCowNode = osgDB::readRefNodeFile("cow.osg");
if (nullptr == spCowNode)
{
osg::notify(osg::FATAL) << "cow node is null\r\n";
return 1;
}
auto pGroup = new osg::Group();
ref_ptr<osg::Switch> spSwitchNode = new osg::Switch();
spSwitchNode->addChild(spCowNode);
spSwitchNode->setValue(0, false); // 第1个参数表示孩子节点在osg::Switch节点中的索引,因为只有一个奶牛节点,所以索引为0
pGroup->addChild(spSwitchNode);
osgViewer::Viewer viewer;
viewer.setSceneData(pGroup);
return viewer.run();
}
说明:
- osg::Switch中的setValue函数的第1个参数表示孩子节点在osg::Switch节点中的索引,因为只有一个奶牛节点,所以索引为0。
- osg::Switch中的setValue函数的第2个参数表示是隐藏还是显示该孩子节点,true为显示;false表示隐藏。
3. NodeMask和Switch混用
如下代码:
// showOrHideNode.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<osgViewer/Viewer>
#include<osg/ArgumentParser>
#include<osgDB/readFile>
#include<osg/Group>
#include<osg/Switch>
#include<osg/PositionAttitudeTransform>
using namespace osg;
int main()
{
auto spCowNode = osgDB::readRefNodeFile("cow.osg");
if (nullptr == spCowNode)
{
osg::notify(osg::FATAL) << "cow node is null\r\n";
return 1;
}
auto pGroup = new osg::Group();
pGroup->addChild(spCowNode);
osgViewer::Viewer viewer;
auto pCamera = viewer.getCamera();
auto defaultCullMask = pCamera->getCullMask(); // 相机默认的裁剪掩码
auto defaultCowNodeMask = spCowNode->getNodeMask(); // 奶牛节点默认的掩码
std::cout.setf(std::cout.hex, std::cout.basefield); // 以16进制输出掩码值
std::cout.setf(std::cout.showbase); // 显示16进制前面的ox
std::cout << "default Cull Mask of camera is : " << defaultCullMask << std::endl;
std::cout << "default Mask of cow node is : " << defaultCowNodeMask << std::endl;
spCowNode->setNodeMask(0x0); // 隐藏奶牛节点
ref_ptr<osg::PositionAttitudeTransform> spPosNode = new osg::PositionAttitudeTransform;
spPosNode->addChild(spCowNode);
spPosNode->setPosition(osg::Vec3d(10.0, 0.0, 0.0));
ref_ptr<osg::Switch> spSwitchNode = new osg::Switch();
spSwitchNode->addChild(spPosNode);
spSwitchNode->setValue(0, true); // 显示奶牛节点
pGroup->addChild(spSwitchNode);
viewer.setSceneData(pGroup);
return viewer.run();
}
说明:
- 上述代码NodeMask和Switch节点都将同一个奶牛节点作为其孩子节点,并进行隐藏与显示。如果NodeMask和Switch节点同时对同一节点进行显示隐藏控制,则最终显示隐藏的结果以NodeMask为主,即NodeMask比Switch优先级高。所以上述代码最后通过Switch显示了奶牛,但由于32行设置NodeMask为0,即不显示奶牛,因为NodeMask比Switch优先级高,所以奶牛最终不会显示。
- 上述代码NodeMask和Switch节点都将同一个奶牛节点作为其孩子对象,即共享同一个奶牛孩子节点,类似C++中的浅拷贝。如果需要分别指向不同的奶牛节点,则应该像下面代码一样,分别挂接一个奶牛节点,类似C++的深拷贝,此时对各自奶牛的显示与隐藏互不影响:
// showOrHideNode.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<osgViewer/Viewer>
#include<osg/ArgumentParser>
#include<osgDB/readFile>
#include<osg/Group>
#include<osg/Switch>
#include<osg/PositionAttitudeTransform>
using namespace osg;
int main()
{
auto spCowNode = osgDB::readRefNodeFile("cow.osg");
if (nullptr == spCowNode)
{
osg::notify(osg::FATAL) << "cow node is null\r\n";
return 1;
}
auto pGroup = new osg::Group();
pGroup->addChild(spCowNode);
osgViewer::Viewer viewer;
auto pCamera = viewer.getCamera();
auto defaultCullMask = pCamera->getCullMask(); // 相机默认的裁剪掩码
auto defaultCowNodeMask = spCowNode->getNodeMask(); // 奶牛节点默认的掩码
std::cout.setf(std::cout.hex, std::cout.basefield); // 以16进制输出掩码值
std::cout.setf(std::cout.showbase); // 显示16进制前面的ox
std::cout << "default Cull Mask of camera is : " << defaultCullMask << std::endl;
std::cout << "default Mask of cow node is : " << defaultCowNodeMask << std::endl;
pCamera->setCullMask(0x10); // 这里将相机裁剪掩码设置为0,即所有节点都被裁剪,不被渲染,此时视景器中的场景是空的,任何场景都不会绘制
spCowNode->setNodeMask(0x01);
ref_ptr<osg::Switch> spSwitchNode = new osg::Switch();
auto spCowNode2 = osgDB::readRefNodeFile("cow.osg");
if (nullptr == spCowNode2)
{
osg::notify(osg::FATAL) << "cow node is null\r\n";
return 1;
}
ref_ptr<osg::PositionAttitudeTransform> spPosNode = new osg::PositionAttitudeTransform;
spPosNode->addChild(spCowNode2);
spPosNode->setPosition(osg::Vec3d(10.0, 0.0, 0.0));
spSwitchNode->addChild(spPosNode);
spSwitchNode->setValue(0, true);
pGroup->addChild(spSwitchNode);
viewer.setSceneData(pGroup);
return viewer.run();
}