osg中漫游器的原理(一)
在osg中,编写以下简单代码
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
viewer->setSceneData(osgDB::readNodeFile("glider.osg"));
viewer->run();
运行可以看到场景中的滑翔机,并通过鼠标操作它的姿态。你有没有觉得很奇怪,使用opengl代码在渲染函数中写下绘制代码时,得到的场景是静止的,需要事件处理函数才可以控制场景的变化。
事实上如此简单的代码,osg替我们作了大量的工作,包括添加了用来操作场景的漫游器。下面我们介绍一下漫游器的相关内容:
- 基本理论
首先要理解OpenGL中的模型视图矩阵的概念,在OpenGL中,通过模型视图矩阵,我们将顶点数据从局部坐标系转换到相机坐标系统,OpenGL中模型视图矩阵是一体的,或者说可以理解为一体。但在OSG中它把这两者分离开来,OSG通过场景的树状结构实现模型矩阵(通过场景中的MatrixTransform),把视图矩阵放在漫游器中,通过两者产生模型视图矩阵。关于视图矩阵,有一个重要的结论:相机在世界坐标系统中的位置姿态矩阵等于相机观察矩阵(视图矩阵)的逆矩阵。相机的位置姿态矩阵可以理解为相机坐标系统下的顶点向世界坐标系统转换的变化矩阵,而观察矩阵(视图矩阵)则理解为世界坐标系的顶点转换到相机坐标系中的变化矩阵。对于这个理论的推导,可以参考:推导相机变换矩阵
另外几个需要了解的问题是:1)OSG中的漫游器是到底是什么?2)漫游器是在什么时候被添加的?3)漫游器是怎么起作用的? 4)我们应该怎么样编写自己的漫游器?下面我们就一一分析
- OSG漫游器是什么
viewer.setCameraManipulator(new osgGA::TrackballManipulator);
- 漫游器什么时候被添加
int Viewer::run()
{if (!getCameraManipulator() && getCamera()->getAllowEventFocus()){setCameraManipulator(new osgGA::TrackballManipulator());}setReleaseContextAtEndOfFrameHint(false);return ViewerBase::run();
}
void View::setCameraManipulator(osgGA::CameraManipulator* manipulator, bool resetPosition)
{_cameraManipulator = manipulator;if (_cameraManipulator.valid()){_cameraManipulator->setCoordinateFrameCallback(new ViewerCoordinateFrameCallback(this));if (getSceneData()) _cameraManipulator->setNode(getSceneData());if (resetPosition){osg::ref_ptr<osgGA::GUIEventAdapter> dummyEvent = _eventQueue->createEvent();_cameraManipulator->home(*dummyEvent, *this);}}
}
可以看到这个函数同时设置了home位置,也就是说如果我们自己的漫游器想有一个初始的位置,那么可以重新实现home这个虚函数来达到这样的目的。
- 漫游器是如何起作用的
void ViewerBase::frame(double simulationTime)
{if (_done) return;// OSG_NOTICE<<std::endl<<"CompositeViewer::frame()"<<std::endl<<std::endl;if (_firstFrame){viewerInit();if (!isRealized()){realize();}_firstFrame = false;}advance(simulationTime);eventTraversal();updateTraversal();renderingTraversals();
}
很显然漫游器的对事件的处理应该是在eventTraversal函数中,漫游器更新的代码应该在updateTraversal中,事实上确实如此:
for(osgGA::EventQueue::Events::iterator itr = events.begin();itr != events.end();++itr){osgGA::Event* event = itr->get();if (event && _cameraManipulator.valid()){_cameraManipulator->handle( event, 0, _eventVisitor.get());}}
同样在更新完场景中节点的更新回调与遍历之后,在函数的最后处理漫游器的更新:
if (_cameraManipulator.valid()){setFusionDistance( getCameraManipulator()->getFusionDistanceMode(),getCameraManipulator()->getFusionDistanceValue() );_cameraManipulator->updateCamera(*_camera);}
updateCamera函数的调用如下(默认实现方式 )
virtual void updateCamera(osg::Camera& camera) { camera.setViewMatrix(getInverseMatrix()); }
直接调用相机的观察矩阵,矩阵从漫游器的getInverseMatrix函数中获取,这是我们编写自己漫游器的关键函数,这个函数在所有漫游器基类CameraManipulator中是一个纯虚函数,需要我们实现,它被调用的位置就是在此处。
- 如何实现自己的漫游器
#include <osgGA/CameraManipulator>//定义操作器
class ZoomManipulator : public osgGA::CameraManipulator
{
public://构造函数传入节点计算包围盒ZoomManipulator(osg::Node *node);~ZoomManipulator();//所有漫游器都必须实现的4个纯虚函数virtual void setByMatrix(const osg::Matrixd& matrix){}virtual void setByInverseMatrix(const osg::Matrixd& matrix){}virtual osg::Matrixd getMatrix() const{return osg::Matrix();}virtual osg::Matrixd getInverseMatrix() const;//获取传入节点,用于使用CameraManipulator中的computeHomePositionvirtual const osg::Node* getNode() const { return _root; }virtual osg::Node* getNode() { return _root; }virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& us);osg::Vec3 _eye; //视点位置osg::Vec3 _direction; //视点方向osg::Vec3 _up; //向上方向osg::Node* _root;
};
在实现代码中通过计算鼠标点处的世界坐标,使视点沿着与鼠标点世界坐标的连线移动
#include <osgViewer/Viewer>ZoomManipulator::ZoomManipulator(osg::Node *node)
{_root = node;computeHomePosition();_eye = _homeEye;_direction = _homeCenter - _homeEye;_up = _homeUp;
}ZoomManipulator::~ZoomManipulator()
{}osg::Matrixd ZoomManipulator::getInverseMatrix() const
{osg::Matrix mat;mat.makeLookAt(_eye, _eye + _direction, _up);return mat;
}bool ZoomManipulator::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& us)
{switch(ea.getEventType()){case (osgGA::GUIEventAdapter::SCROLL):{osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(&us);osg::Camera *camera = viewer->getCamera();osg::Matrix MVPW = camera->getViewMatrix() * camera->getProjectionMatrix() * camera->getViewport()->computeWindowMatrix();osg::Matrix inverseMVPW = osg::Matrix::inverse(MVPW);osg::Vec3 mouseWorld = osg::Vec3(ea.getX(), ea.getY(), 0) * inverseMVPW;osg::Vec3 direction = mouseWorld - _eye;direction.normalize(); if (ea.getScrollingMotion() == osgGA::GUIEventAdapter::SCROLL_UP){_eye += direction * 20.0;}else if (ea.getScrollingMotion() == osgGA::GUIEventAdapter::SCROLL_DOWN){_eye -= direction * 20.0;}}default:return false;}
}
osg中漫游器的原理(一)相关推荐
- 【转】osg中漫游器的原理
在osg中,编写以下简单代码: 1 osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer(); 2 viewer-& ...
- osg漫游器的原理和执行流程
关键词:manipulator; 漫游器; 第一人称漫游器; osg; osg漫游器的原理; osg漫游器的流程 从CameraManipulator讲起吧,CameraManipulator是漫游器 ...
- [osgearth][原]仿照谷歌,修改oe漫游器中focal(视角切换)功能
oe中的视角加速感觉好奇怪,就仿照谷歌方式去改了. 先看看oe原来的漫游器改变视角的接口: void CameraManipulator::setViewpoint(const Viewpoint&a ...
- python装饰器原理-python 中的装饰器及其原理
装饰器模式 此前的文章中我们介绍过装饰器模式: 装饰器模式中具体的 Decorator 实现类通过将对组建的请求转发给被装饰的对象,并在转发前后执行一些额外的动作来修改原有的部分行为,实现增强 Com ...
- OSG路径漫游实现与应用
路径漫游简介 路径漫游在OSG中即为视点或者模型按照我们自己设定的路径进行漫游的一种方法,原本在OsgViewer中就有,在第一次按键盘小写"z"开始记录动画路径,大写" ...
- OSG场景漫游(一)
一.编写动态链接库TravelManipulator.DLL 第一步:新建->项目->WIN32/WIN32控制台应用程序,项目名称填入:TravelManipulator在应用程序设置中 ...
- 漫游器在椭球上的定位和变换
漫游器的使用场景非常丰富,osg的osgGA库中包含了许多常用的漫游器类型,如:最常见的TrackballManipulator(轨迹球漫游器).DriveManipulator(驾驶漫游器).Fir ...
- javascript原理_JavaScript程序包管理器工作原理简介
javascript原理 by Shubheksha 通过Shubheksha JavaScript程序包管理器工作原理简介 (An introduction to how JavaScript pa ...
- 超专业解析!10分钟带你搞懂Linux中直接I/O原理
导语 | 本文主要以一张图为基础,向大家介绍Linux在I/O上做了哪些事情,即Linux中直接I/O原理,希望本文的经验和思路能为读者提供一些帮助和思考. 引言 我们先看一张图: 这张图大体上描述了 ...
最新文章
- PAT1066 Root of AVL Tree (25)(AVL树)
- 2021-02-23 Matlab数据导入--importdata和load函数
- Zookeeper和 Google Chubby对比分析
- Navicat连接sqlserver提示:未发现数据源名并且未指定默认驱动程序
- 使用Java 8防止日志过宽
- 微服务分布式学到这种程度,稳了!
- php查看mysql最近执行过的sql_PHP获取MySQL执行sql语句的查询时间
- Thrift原理与使用实例
- mysql(6)-mysql的视图功能和存储过程
- python为什么要安装pip_为什么您应该使用`python -m pip`
- 数据挖掘算法和实践(三):朴素贝叶斯(mushrooms蘑菇数据集)
- hp服务器修改bios做系统,hp 服务器bios设置
- 作为iOS开发者,你不可错过的资源
- Flexbox布局基础入门
- 稿费一般多少钱一千字_写网络小说能挣多少钱,稿费都是怎么算的?
- 58同城 Flutter 混合开发探索与实践
- 云端3d虚拟现实展制作
- 全球医药研发支出及处方药市场发展前景分析:预计到2026年全球处方药销售额超过1.4万亿美元[图]
- 如何查询SCI和EI检索号
- React-mentions 基本使用
热门文章
- 【ZED】从零开始使用ZED相机(五):Opencv+Python实现相机标定(双目)
- 从全职高手开始的系统_第一章 全职高手系统
- 最小公约数(欧几里得算法stein算法)
- 疫情下,京津复工通勤指南
- 计算机网络私有地址吗,公有IP地址与私有IP地址有什么不同?
- 大一下 c + + 上机实验总结(五)
- SpringBoot (二) 整合前端模板引擎FreeMarker、thymeleaf
- 奇思妙想之关于淘宝购衣
- mysql会话和事务_MySQL事务熟练使用就够?和腾讯大佬的一席对话,原来考点都在这些方面!...
- 边城科技助力湘西州迎来“区块链+”时代