OSG节点类Group、拷贝和Cull——b站OSG72讲7-8节,25-26节(书籍示例后补)

文章详细介绍了OpenSceneGraph(OSG)中的派生节点类,如矩阵变换、位置变换、自动对齐、开关、细节层次、分页细节层次、遮挡裁剪、坐标系和相机节点等,以及如何使用它们进行空间变换、透明体处理和渲染性能优化。
摘要由CSDN通过智能技术生成

一、osg::Group的派生节点类

0.osg::Transform

在这里插入图片描述

  • 绝对参考系RELATIVE_RF中,几何对象的变换只作用于全局坐标系/世界坐标系中。

例如每多画一层楼,必须得到每一层楼距离地面的实际位置

  • 相对参考系ABSOLUTE_RF中,几何对象的变换作用于上一级对象的局部坐标系中。

例如每多画一层楼,只需要获得每一层相对于底层的偏移距离,因为上一层已经对于地面偏移。

  • 如果要将某一级局部坐标系下的顶点V变换为世界坐标V’,则需要将各级局部坐标系的变换矩阵依次相乘,假设在世界坐标系下变换矩阵M0,依次累积,直至V所在的局部坐标系的变换矩阵Mn,那么级联矩阵如下:注意矩阵相乘没有交换律!
    在这里插入图片描述
    osg::Transform就是为了不需要过多考虑矩阵堆栈而设计的
    在这里插入图片描述
    它在场景树中实现空间变换作用的核心函数,场景树的各级Transform节点依次传递computeLocalToWorldMatrix中的matrix参数,将自己的变换矩阵与之联乘,最终即可得到几何顶点位置在世界中的真实坐标,其中对绝对参考系和相对参考系有做不同的运算。

1.矩阵变换节点osg::MatrixTransform

主要作用是负责场景中矩阵变换、矩阵的运算及坐标系的变换,动画更新也是用MatrixTransform来设置移动和旋转。
通过矩阵变换节点也可以对场景中模型进行旋转、平移操作,和osg::PositionAttitudeTransform效果一样,但是MatrixTransform更直观,PositionAttitudeTransform更偏重于坐标。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

绝对坐标系直接当前MatirxTransform节点的成员矩阵加入运算,但是如果当前是参考坐标系,则需要与当前MatirxTransform节点的成员矩阵进行左乘形成级联矩阵,参考在这里插入图片描述

在这里插入图片描述

为什么相对坐标系要右乘参考:在这里插入图片描述

已知viewer.run()是执行主帧循环,等同于:while (!viewer.done()) viewer.frame();
模型数据一般在run之前就加载好了,如果需要在 run中 进行变换等操作,需要注册回调函数(即callback,约定熟成在run中会自动调用的函数):
在这里我们需要用osg::MatrixTransform::setUpdateCallback注册一个回调函数来实现简单的动画效果。

示例:

osg::ref_ptr<osg::Node> MatrixOperation()
{
	osg::ref_ptr<osg::Node> node  = osgDB::readNodeFile("glider.osg");
	
	osg::ref_ptr<osg::MatrixTransform> mat = new osg::MatrixTransform;
	osg::ref_ptr<osg::MatrixTransform> mat2 = new osg::MatrixTransform;
	
	// 关联
	mat2->addChild(node);
	mat->addChild(mat2);
	
	// 在mat中注册一个效果为旋转的回调函数
	mat->setUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(5.0,0.0,0.0),osg::Z_AXIS,1.0));
	
	mat2->setMatrix(osg::Matrix::translate(5.0,0.0,0.0));
	return mat;
}

之所以要将setMatrix和addChild向下挪一个级别,是因为setMatrix,addChild,callback同一个级别时,由于PATHCALLBACK自己也会生成矩阵信息,将会覆盖掉setMatrix。
在这里插入图片描述

2.位置变换节点osg::PositionAttitudeTransform

主要作用是提供模型的位置变换、大小缩放、原点位置的设置以及坐标系的变换。
“让某个物体沿X轴运动10个单位,再绕Z轴旋转半圈”这种直观方法。
在这里插入图片描述
在这里插入图片描述

需要遵循SRT的运算顺序来完成复合矩阵的构建
在这里插入图片描述

在这里插入图片描述

所以永远都是先按照原点反方向操作了模型之后,再进行SRT,那么就引出来下面的示例2。

示例1:

osg::ref_ptr<osg::Node> MatrixOperation()
{
	// 这里用最基础的Node即可,不需要Geode的操作
	osg::ref_ptr<osg::Node> node  = osgDB::readNodeFile("D:\\vs2019 64位 3rdParty osg365 oe32\\OpenSceneGraph-Data\\glider.osg");
	
	// 创建位置变换节点
	osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform;
	// 设置位置
	pat->setPosition(osg::Vec3(10.0,0.0,0.0));
	// 设置缩放
	pat->setScale(osg::Vec3(10.0, 10.0, 10.0));
	// 添加子节点
	pat->addChild(node);

	return pat;
}

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
	osg::ref_ptr<osg::Group> group = new osg::Group;
	// 想要blend(混合测试)有预期的效果,必须先渲染所有不透明的,再渲染深度值从大到小的透明的!这样子才能通过深度测试。
	group->addChild(osgDB::readNodeFile("D:\\vs2019 64位 3rdParty osg365 oe32\\OpenSceneGraph-Data\\glider.osg"));
	group->addChild(MatrixOperation());

	viewer->setSceneData(group.get());
	return viewer->run();
}

示例2:

注意的是这个类一般对处于原点的模型才会有预期效果。
如果想要真正能按预期效果的,应该按照如下:
在这里插入图片描述

// 从osg::MatrixTransform派生一个类,一下为成员函数
// 当前模型的转动方式
void rotating(const osg::Vec3d &pivot, const osg::Vec3d &axis, float angularVelocity)
{	
	this->setUpdateCallback(new osg::AnimationPathCallback(pivot,axis,angularVelocity));
}

... ...
osg::Node *m_node =/*加载模型*/;
osg::BoundingSphere m_ps = node->getBound();
float m_level = 1.0;// 缩放因子

// 移动模型
void toPosition(const osg::Vec3d &pos)
{
	osg::Vec3d cps;
	cps.set(-m_ps.center().x()*m_level ,-m_ps.center().y()*m_level ,-m_ps.center().z()*m_level );
	 this->setMatrix(osg::Matrix::translate(cps)*osg::Matrix::translate(pos));// 先移回原点
}

// 限制模型大小
void adapt(const osg::BoundingSpere& sps)
{
	m_level = sps.radius()/ps.radius();
	this->setMatrix(osg::Matrix::scale(m_level ,m_level ,m_level ));
}
void adapt(const osg::Node *node)
{
	osg::BoundingSpere sps = node->getBound();
	m_level = sps.radius()/ps.radius();
	this->setMatrix(osg::Matrix::scale(m_level ,m_level ,m_level ));
}

// 旋转模型
void toRotate(const Matrix& mat)
{
	 this->setMatrix(mat);
}

// 缩放模型
void toScale(const Matrix& mat)
{
	 this->setMatrix(mat);
}

3.自动对齐节点osg::AutoTransform

主要作用是使节点自动对齐与摄像机或屏幕,通常用来显示一些不变化的文字或者其他的标识。

enum AutoRotateMode
{ NO_ROTATION, // 无旋转
ROTATE_TO_SCREEN, // 自动朝向屏幕
ROTATE_TO_CAMERA, // 自动朝向相机
ROTATE_TO_AXIS // 自动朝向轴
}

4.开关节点osg::Switch,类似图层概念

使用开关节点可以渲染或者跳过指定子节点,且能够根据当前渲染的负荷有选择地渲染子场景以实现渲染性能的均衡,或者在游戏的界面和层级之间有选择地切换。
如果想在应用程序中实现允许或禁止各种开关节点的渲染,需要调用节点更新回调或者节点访问器来控制其渲染状态。

void 	setNewChildDefaultValue (bool value)// 设置新加节点的初始值
bool 	getNewChildDefaultValue () const
void 	setValue (unsigned int pos, bool value)// 设置索引为pos的开关值,false忽略该节点
bool 	getValue (unsigned int pos) const
void 	setChildValue (const Node *child, bool value)
bool 	getChildValue (const Node *child) const
bool 	setAllChildrenOff ()// 设置所有子节点不显示
bool 	setAllChildrenOn ()
bool 	setSingleChildOn (unsigned int pos)// 设置索引为pos的单个节点显示

其实是通过void Switch::traverse(NodeVisitor& nv)来实现某个子节点的开关值确实地作用在这个节点上:
在这里插入图片描述

示例:

class CessnaCallback :public osg::NodeCallback
{
public:
	static const int _fireStartFrame = 120; 
	virtual void operator()(osg::Node *node, osg::NodeVisitor *nv)
	{
		osg::Switch *cessnaSwitch = dynamic_cast<osg::Switch *>(node); 
		if (cessnaSwitch &&nv)
		{
			//getFrameStamp()随时取得当前的运行帧数
			const osg::FrameStamp *frameStamp = nv->getFrameStamp();
			if (frameStamp)
			{
				if (_fireStartFrame < frameStamp->getFrameNumber()) 
				{
					//setValue()执行子节点的切换
					cessnaSwitch->setValue(0, false);
					cessnaSwitch->setValue(1, true);
				}
			}
		}
		traverse(node, nv); //向下一个需要访问的节点推进
	}
};

int main(int argc, char **argv)
{
	
	osg::ref_ptr<osg::Switch> root = new osg::Switch;
	root->addChild(osgDB::readNodeFile("cessna.osg"), true);
	root->addChild(osgDB::readNodeFile("cessnafire.osg"), false);
	
	// 在更新遍历中对开关节点及其节点做扩展处理
	root->setUpdateCallback(new CessnaCallback);
 
	osgViewer::Viewer viewer;
	
	viewer.setSceneData(root.get());

	return viewer.run();
}

5.细节层次节点osg::LOD:osg::Group

可以实现不同层次下物体的渲染,基本思想是使用物体的一种简单形式表达物体,这样可以使绘制的图形尽量简洁。
例如,当视点靠近物体时,用详细的细节表示;当视点远离物体时,用简化模型来表示。
可以获得加速效果,较快的绘制速度和提高帧率。

实际上和开关节点类似,也是通过访问器判断当前节点及其字节带你是否会被访问器触及到。
在这里插入图片描述

在OSG中,对于一个LOD模型,它是一次性载入内存,只是有选择地绘制渲染。
在这里插入图片描述

在这里插入图片描述

LOD计算的是视点到物体包围盒中心的距离,在应用程序中也可以改变这种方式,它有三种中心模式。

enum  	CenterMode 
{ 
USE_BOUNDING_SPHERE_CENTER, // 包围盒中心
USER_DEFINED_CENTER, // 自定义中心,需要自己调用setCenter()
UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED 
}

LOD的切换同样是根据距离来确定的,应用程序中同样可以根据屏幕像素大小来切换,它有两种变换模式。

enum  	RangeMode 
{ 
DISTANCE_FROM_EYE_POINT, // 距视点的距离
PIXEL_SIZE_ON_SCREEN // 屏幕像素的大小
}

在这里插入图片描述
lod节点的处理方法。

示例:

int main()
{
	osg::ref_ptr<osg::Group> group = new osg::Group;
	osg::ref_ptr<osg::Node> node1 = osgDB::readNodeFile("glider.osg");
	osg::ref_ptr<osg::Node> node2 = osgDB::readNodeFile("cow.osg");
	
	// 创建一个细节层次节点LOD节点
	osg::ref_ptr<osg::LOD> lode = new osg::LOD();
	// 在0-30范围显示牛
	lode->addChild(node1.get(),0.0f,30.0f);
	lode->addChild(node2.get(), 30.0f, 100.0f);

	// 写入lode.osg文件
	osgDB::writeNodeFile(*(lode.get()),"lode.osg");

	group->addChild(lode.get());
	viewer->addEventHandler(new osgViewer::HelpHandler);
	viewer->setSceneData(group.get());
	return viewer->run();
}

6.分页细节层次节点osg::PagedLOD:osg::LOD(动态调度节点)

用于实现动态分页加载,根据视点来加载所需要的,分页细节层次节点还可以包含LOD节点。

它与osg::LOD的区别在于:
osg::LOD节点存在于一个文件之中,osg::PagedLOD的每个节点都是磁盘中的文件,可以根据需要来加载这些文件,加载过程中有单独的线程负责实时调度及加载。
即PagedLOD节点只提供索引的作用,每个LOD节点存在与磁盘文件中,但不把文件存在一个文件中。

osg::PagedLOD主要是用来处理大规模的数据,在地形和GIS方面有广泛的应用,可以把模型进行预处理,在渲染场景时,再根据需要来实时加载需要的数据及卸载无用的数据

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
具体使用我看到另一个博主的

7.替代节点osgSim::Impostor : osg::LOD

也是一种布告板,它是通过从当前的视点将一个复杂物体对象绘制到一副图像纹理上来创建的,其中渲染的图像纹理映射到布告板上。

可用于物体的一些实例或一些画面,可以加速图像的绘制及渲染(绘制一个替代节点一般比绘制其所表示的物体要快),还有个优点时可以对纹理图像进行低通滤波,从而生成一张可用于景深效果的模糊图像
可以处理视点较远的缓慢运动物体和运动时有一面总是面向视点的物体

通常要对替代物通过分辨率来进行有效限制,图形纹理的分辨率绝对不能超过屏幕当前的分辨率。
当一个远处的替代节点慢慢靠近时,纹理中的单个像素将变得越明显,原来的视觉效果就会消失,这时就需要根据视点和替代节点的当前位置生成新的替代物:

void 	setImpostorThreshold (float distance)// 这个函数用于设置开始启动Impostor的距离,参数可以根据情况适当调节。

可以有多个子节点来控制模型的层次显示。

8.遮挡裁剪节点osg::OccluderNode

主要作用时裁剪掉被遮挡的物体。
该类的子节点用于表达遮挡物的形状结构,自己不会被裁减掉,但是场景中的其他对象可能会被该节点遮挡而被自动裁减掉。

一般深度测试中,Z缓冲区只选择并绘制那些可见的物体,但是必须将这些物体都送入渲染管线(而深度测试在片段着色器之后),导致效率低下。
高效的遮挡算法背后的思想是:提前执行一些简单的测试,从而避免将所有的数据送入管线的大部分阶段。

9.坐标系节点sog::CoordinateSystemNode

主要作用是使一个场景的对象关联一个坐标系统(WKT、PROJ4和USGS),通常与osg::EllipsoidModel节点一起使用。

osg::EllipsoidModel节点: 椭圆体模型节点,主要用于模拟天体,如太阳、行星和月亮等,默认是地球。
还有一个作用是实现经纬度和坐标之间的转换,这样子可以实现天体椭圆体模型子节点的精确定位。
当建立地球数据库时,这个节点会非常有用,可以根据地球椭圆体模型实现子节点的精确定位。

10.相机节点(例如正视,俯视角度)osg::Camera

在这里插入图片描述
osg::Camera节点的所有子节点都会被渲染到这个相机的视窗中。

10.投影节点osg::Projection

将子场景投影到一个二维平面上,不同于Camera节点因为相机的运行会影响所有子节点的空间位置姿态,所以需要变换节点,本节点是投影到二维平面上,是不需要考虑Projection节点自身的位置和姿态变换的。

11.渲染属性节点osg::StateSet

该节点设置的光照,材质,纹理等属性都会作用在每一个子节点上。但是可能会增加场景的耦合度和复杂度。
在这里插入图片描述
OSG采用为每个节点设置渲染状态集StateSet来管理渲染属性,而不是直接派生自Group类的功能节点,而父节点的渲染属性同样可以 有选择地继承到子节点上面,从而解决了可能会导致增加耦合度的问题。

12.覆盖节点osgSim::OverlayNode

其子节点渲染结果覆盖到了另一个场景的指定对象上,例如电影院将三维场景覆盖到了电影幕布上。

13.代理节点ProxyNode

被代理的对象(可能是很复杂的一个模型),在它的节点类尚为加载时,仍然会在适当的时候被加载到内存中,参考CAD中的代理对象同样的效果。

14.osg::Billbroad

继承于Geode节点,包含3种模式:
在这里插入图片描述
布告板实现原理:将图形绘制在朝向视点的多边形表面上,根据视点的观察方向来确定多边形的方向,随着观察视角的变换,多边形的形状也随之变换

osg::Billbroad与Alpha纹理和动画融合技术可以实现很多没有实心表面的现象,例如粒子系统中烟、火、雾、爆炸、雨、雪及云朵等。
主要包括面向世界(面向视平面,面向视点)和轴向布告板。
在这里插入图片描述

15.场景中节点的拷贝osg::CopyOp(补

16.自由度节点DOFTransform

它通过对3种基本旋转量,即航偏角度(Yaw,沿Z轴旋转),摆渡角度(Pitch,沿Y轴旋转),横滚角度(Roll,沿X轴旋转)的复合运算,得到以旋转为主的空间变换结果。

二、Cull

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
	osg::ref_ptr<osg::Group> group = new osg::Group;
	osg::ref_ptr<osg::Node> nodeGilder = osgDB::readNodeFile("glider.osg");
	// 两个透明方块
	osg::ref_ptr<osg::Node> node1 = CreateShape(osg::Vec3(0.0,2.0,0.0));
	osg::ref_ptr<osg::Node> node2 = CreateShape(osg::Vec3(0.0, -2.0, 0.0));

	// 设置成透明体,才能正常拣选
	node1->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
	node2->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
	
	group->addChild(node1);
	group->addChild(node2);group->addChild(nodeGilder);

}
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值