OSG入门教学

OSG——场景

  • List itemOSG的场景是以树形组织的,最上层为根节点,根节点只能有一个,根节点Node类型,一般使用Group
  • 根节点有很多分支,每个分支可以再有分支,每个分支点最上层的节点可以被看作该分支的根节点,用于管理整个分支的状态信息(光照,融合,透明等),为Node类型,一般使用Group;
  • 每个分支末端会是一个叶节点,叶结点用于管理绘制体,叶结点为Geode或其继承类(Billboard)
  • 可绘制体保存有绘制信息,例如几何体,文字,像素等,绘制体由叶结点管理,必须添加到叶结点才能最终被OSG渲染到窗口显示

1.依赖项配置

//Common.h
#ifndef _COMMON_H_
#define _COMMON_H_#indef _DEBUG
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "OoenThreadsd.lib")
#else
#pragma comment(lib, "osg.lib")
#pragma comment(lib, "osgDB.lib")
#pragma comment(lib, "osgdViewer.lib")
#pragma comment(lib, "OoenThreads.lib")
#endif#endif

2.超级指针

有一个引用指向该资源,该资源就不应该被释放。所以,大多数的 OSG 对象都有一个内部的计数器用来计算有多少个指针指向它。当没有指针指向该资源(同样,可以称做对象)时,它的计数器会变为 0,当计算器为 0 时就可以放掉对象。幸运的是,我们程序员不需要手工的来维护这些引用计数:这就是超级指针为什么存在的原因。在 OSG 当中超级指针被一个称为 osg::ref_ptr<>的类模版来实现。无论在什么时候,只要该对象被引用,则在 osg::ref_ptr<>当中的计数器会自动增加, 通过这种方式实现了资源的自动管理,当不需要使用它时,它会在不久后释放,在申请它时会自动分配空间。

int main(){osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;osg::ref_ptr<osg::Node> node = new osg::Node;node = osgDB::readNodeFile("glider.osg");viewer->setSceneData(node);return viewer->run();
}

3.响应事件

之前通过主函数main进行视图管理(osgViewer),利用osgDB进行模型读写,将读写的模型放在结点指针中,再将结点指针放在视图中,最后运行视图(run)从而达到显示模型的目的;

现在增加一个事件管理(osgGA),基于事件的小应用(EventHandler);
1.运行glider模型
在这里插入图片描述
2.点击‘h’按键,显示文字,如下图
在这里插入图片描述

#include
#include
//这两个是有关事件管理的头文件//添加帮助事件
viewer->addEventHandler(new osgViewer::HelpHandler);

此时运行项目,点击h后会出现文字信息,但却只有几行,这是因为osg在帮助事件的类中设置了相应显示文字的行数,可根据需要更改,这里

4.我们模拟一个事件类,响应单击h

//模拟一个事件类,相应单击h
class PrintName : public osgGA::GUIEventHandler
{
public:virtual void getUsage(osg::ApplicationUsage& usage) const {usage.addKeyboardMouseBinding("lk", "Hello!");//显示文字的最后一行内容}
};viewer->addEventHandler(new osgViewer::HelpHandler);  //显示帮助文档的事件
viewer->addEventHandler(new PrintName);//显示我们新建的事件类

还有事件类如下:

	viewer->addEventHandler(new osgViewer::StatsHandler);viewer->addEventHandler(new osgViewer::WindowSizeHandler);viewer->addEventHandler(new osgViewer::ScreenCaptureHandler);

5.定时器的使用

//申请一个定时器类osg::Timer  * timer = new osg::Timer;osg::Timer_t start_time = 0;osg::Timer_t end_time = 0;//得到一个tick值为多少secondstd::cout << timer->getSecondsPerTick() << std::endl;start_time = timer->tick();//读取模型node = osgDB::readNodeFile("glider.osg");end_time = timer->tick();std::cout << "读取模型时间为: " << timer->delta_s(start_time, end_time) << std::endl;

注意,在申请定时器类时,不能使用超级指针:

osg::ref_ptr<osg::Timer> timer = new osg::Timer;  //错误示范,有些类不支持超级指针

第二种计算读取模型时间的方法:

	start_time = timer->tick();timer->setStartTick();//读取模型node = osgDB::readNodeFile("glider.osg");std::cout << "方法一读取模型时间为:" <<timer->time_s()<<std::endl;end_time = timer->tick();std::cout << "方法二读取模型时间为: " << timer->delta_s(start_time, end_time) << std::endl;

并且预计方法一时间比方法二短一点,因为方法二多执行了几条语句,所以这样可以计算出中间语句的执行时间。如果在计算程序效率时,可以根据此方法查询是哪条语句耗时太多,从而进行优化或者将其功能删掉。

方法三:利用viewer自带的elapsedTime()函数得出start_time和end_time

float start_time = 0;
float end_time = 0;start_time = viewer=>elapsedTime();
node = osgDB::readNodeFile("cow.osg");
end_time = viewer->elapsedTime();std::cout<<"方法三读取模型时间为: "<<end_time-start_time<<std::endl;

6.操作器

//操作器的头文件
#includeosg::Timer_t start_frame_time = 0;osg::Timer_t end_frame_time = 0;//计算帧速int counts = 0;//加操作器viewer->setCameraManipulator(new osgGA::TrackballManipulator);while (!viewer->done()) {if (counts == 0) {start_frame_time = timer->tick();}counts++;viewer->frame();if (counts == 3) {counts = 0;end_frame_time = timer->tick();std::cout << "当前帧速为: " << 3 / (timer->delta_s(start_frame_time, end_frame_time)) << std::endl;}}return 0;

其中加了个counts变量,因为一帧一帧查看会因为模型静止等原因而与实际帧速不相符,所以我们每三帧取一次值,保证准确性

7.基本图元

这是绘制自带的长方体,对其大小进行设置,在贴一些纹理,达到所需效果

#include
#include
#include
#include
#include //增加图元类
#include //增加材质,让图形更有是体感,更好看
#include 
#include //贴纹理
#include //贴纹理
#includeosg::ref_ptr<osg::Geode> CreateBox() {osg::ref_ptr<osg::Geode> geode = new osg::Geode;osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints; //设置精度类osg::ref_ptr<osg::ShapeDrawable> shape = new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0,0.0,0.0),1.0,10.0,10.0),hints.get()); //建立一个图元osg::ref_ptr<osg::Material> material = new osg::Material;osg::ref_ptr<osg::Texture2D> texture2D = new osg::Texture2D;osg::ref_ptr<osg::Image> image;shape->setColor(osg::Vec4(0.5, 0.5, 0.5, 0.1)); //设置颜色hints->setDetailRatio(0.5); //设置精度//设置材质material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.0, 1.0, 1.0, 0.5));material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.0, 1.0, 1.0, 0.5)); //混合光material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.0, 1.0, 1.0, 0.5)); //反射光material->setShininess(osg::Material::FRONT_AND_BACK, 6.0); //反射面积,一个点还是一个面//设置纹理image = osgDB::readImageFile("Images/whitemetal_diffuse.jpg");if (image.valid()) { //如果纹理图片是可用的texture2D->setImage(image.get());  //则获取该图片}geode->getOrCreateStateSet()->setAttributeAndModes(material.get(), osg::StateAttribute::ON);  //将所有质感设置添加到图元中geode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON); //打开深度测试geode->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); //设置透明度geode->getOrCreateStateSet()->setTextureAttributeAndModes(0,texture2D.get(),osg::StateAttribute::ON);  //添加纹理geode->addDrawable(shape.get());return geode;
}int main() {osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;viewer->setSceneData(CreateBox());//viewer->setSceneData(osgDB::readNodeFile("glider.osg"));return viewer->run();}

接下来自行绘制图形,建立四个点,逆时针相连,得出一个长方形,在为其设置颜色,线宽,以及透明度

/创建简单图元
osg::ref_ptr<osg::Node> CreateSimple() {osg::ref_ptr<osg::Geode> geode = new osg::Geode;osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;//申请一些顶点osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array;//申请一些颜色osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;//申请法向量osg::ref_ptr<osg::Vec3Array> norms = new osg::Vec3Array;//申请线宽osg::ref_ptr<osg::LineWidth> width = new osg::LineWidth;geode->addDrawable(geom.get());//打开透明度geode->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);//设置线宽width->setWidth(15.0);geode->getOrCreateStateSet()->setAttributeAndModes(width.get(), osg::StateAttribute::ON);//设置顶点geom->setVertexArray(coords.get());//设置顶点的关联方式geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::Mode::LINE_LOOP, 0, 4));//绘制四边形,共四个顶点//设置顶点颜色geom->setColorArray(colors.get());geom->setColorBinding(osg::Geometry::AttributeBinding::BIND_PER_VERTEX); //设置绑定方式//设置法向量geom->setNormalArray(norms.get());geom->setNormalBinding(osg::Geometry::AttributeBinding::BIND_PER_VERTEX);//设置顶点;逆时针绘制,x,y,zcoords->push_back(osg::Vec3(-10.0, 5.0,-10.0));//左下点coords->push_back(osg::Vec3(10.0, 5.0, -10.0));  //右下点coords->push_back(osg::Vec3(10.0, 5.0, 10.0)); //右上点coords->push_back(osg::Vec3(-10.0, 5.0, 10.0));  //左上点//颜色colors->push_back(osg::Vec4f(1.0, 0.0, 0.0, 0.5));colors->push_back(osg::Vec4f(0.0, 1.0, 0.0, 0.5));colors->push_back(osg::Vec4f(0.0, 0.0, 1.0, 0.5));colors->push_back(osg::Vec4f(1.0, 1.0, 0.0, 0.5));//压入一个法向量norms->push_back(osg::Vec3(0.0, -1.0, 0.0));return geode;
}int main() {osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;//为了突出四边形透明度,加一个参照物“滑翔机”osg::ref_ptr<osg::Group> group = new osg::Group;group->addChild(osgDB::readNodeFile("glider.osg"));group->addChild(CreateSimple());viewer->setSceneData(group.get());return viewer->run();}

在这里插入图片描述
注意:因为设置了线宽,所以透明度并不明显,将线宽去掉,透明度更为明显,而且为了突出透明效果,加一个滑翔机模型辅助,注意渲染顺序,先加滑翔机,后加长方形。如果顺序颠倒,则滑翔机会覆盖长方形,无法得到透明效果。

8.矩阵操作(Matrix Operation)

8.1 矩阵平移

#include
#include#include
#include
#include
#include //矩阵平移头文件(类)//Matrix Operation
osg::ref_ptr<osg::Node> MatrixOperation() {osg::ref_ptr<osg::Group> group = new osg::Group;  //提供展示多个图元的组osg::ref_ptr<osg::MatrixTransform> max = new osg::MatrixTransform;  //定义一个可供平移的图元对象osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("glider.osg");max->addChild(osgDB::readNodeFile("glider.osg"));  //该图元为小飞机max->setMatrix(osg::Matrix::translate(-1.5, 0.0, 1.5));  //向左平移五个单位group->addChild(node);group->addChild(max);return group;
}int main() {osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;viewer->setSceneData(MatrixOperation().get());return viewer->run();
}

8.2 矩阵旋转(动态)


#include //旋转//Matrix Operation
osg::ref_ptr<osg::Node> MatrixOperation() {...max->setMatrix(osg::Matrix::translate(-1.5, 0.0, 1.5));  //向左平移五个单位max->setUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(0.0, 0.0, 0.0), osg::Z_AXIS, 1.0));group->addChild(node);group->addChild(max);return group;
}

此时便会出现问题,运行后会发现,设置旋转的小飞机在“原点”围绕z轴旋转,旋转没有问题,但位置却不对,因为上一步已经将该飞机平移至(-1.5, 0.0, 1.5),此时平移效果没有了。
原因: 旋转的Callback会刷掉同级的其他变换矩阵(具体不详细解释,因为内部原因我也不明白emmm,简单理解就是覆盖了其他同级的矩阵,因此上面的平移矩阵无效了);同级的意思是:对类对象max(代码中定义的对象)进行的操作,有代码可知,我们对max进行了三个操作:1.加载图元小飞机;2.平移;3.旋转Callback;所以Callback覆盖了“同级”的平移矩阵;

解决代码如下:

sg::ref_ptr<osg::Node> MatrixOperation() {osg::ref_ptr<osg::Group> group = new osg::Group;  //提供展示多个图元的组osg::ref_ptr<osg::MatrixTransform> max = new osg::MatrixTransform;  //定义一个可供平移的图元对象osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("glider.osg");osg::ref_ptr<osg::MatrixTransform> max2 = new osg::MatrixTransform;max2->addChild(osgDB::readNodeFile("glider.osg"));  //该图元为小飞机max2->setMatrix(osg::Matrix::translate(-1.5, 0.0, 1.5));  //向左平移五个单位max->setUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(0.0, 0.0, 0.0), osg::Z_AXIS, 1.0));max->addChild(max2.get());group->addChild(node);group->addChild(max);return group;
}

将加载图元和平移操作放在一个对象B,旋转操作放在对象A,对象A中加入对象B,使得平移和旋转操作不在“同一级”(对象A【根结点】下有两个节点:旋转节点+对象B节点;对象B节点下有两个节点:加载图元+平移。因此不再同一层);

下面主要是对一个模型进行平移修改,然后将结果保存为新的模型;

void ExportANode() {osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("cow.osg");osg::ref_ptr<osg::MatrixTransform> max = new osg::MatrixTransform;max->addChild(node);max->setMatrix(osg::Matrix::translate(50.0, 0.0, 0.0));osgDB::ReaderWriter::WriteResult result = osgDB::Registry::instance()->writeNode(*max.get(), "TrCow.osg", osgDB::Registry::instance()->getOptions());if (result.success()) {osg::notify(osg::NOTICE) << "Write Node Success" << std::endl;}
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部