漫游器(一)

news/2025/1/3 16:45:29/

在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漫游器是什么
漫游器在osgGA库中实现,这个库主要是用来处理用户与三维场景的交互(包括鼠标、键盘、手势、操纵杆等),提供了大量的漫游器展示,文章开头代码中默认添加的轨迹球操作器(TrackballManipulator),这些漫游器都继承自osgGA::CameraManpualtor
在这里插入图片描述
osgGA::CameraManipulator继承自osgGA::GUIEventHandler,我们知道GUIEventHandler是用来处理事件的类,从这一点就可以知道漫游器实际上就是一个交互操作修改场景中节点位置和姿态的类,只不过它修改的是最顶层的相机节点,这个节点的修改影响着整个场景。osg的视景器(view)中管理着GUIEventHanlder的列表,一般添加的方式是使用:addEventHandler这种方式,在添加漫游器的时候如果使用这种方式并没有任何意义,因为用于表达相机观察方位的getMatrix和getInverseMatrix函数永远不会被视景器View调用到,正确添加漫游器的方法是:
viewer.setCameraManipulator(new osgGA::TrackballManipulator);
漫游器什么时候被添加
在文章开头的示例代码中,我们并没有调用setCameraManipulator的代码,那操作器是怎么被添加的呢?答案就在viewer->run()这行代码中,通过查看它的代码得知

int Viewer::run()
{if (!getCameraManipulator() && getCamera()->getAllowEventFocus()){setCameraManipulator(new osgGA::TrackballManipulator());}setReleaseContextAtEndOfFrameHint(false);return ViewerBase::run();
}

当我们在调用run之前没有设置漫游器,osgViewer::Viewer会为我们默认设置一个轨迹球的漫游器。

此外,通过setCameraManipulator函数的实现:

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这个虚函数来达到这样的目的。
漫游器是如何起作用的
漫游器在添加之后,它必须处理输入事件,并且更新场景,要了解这些内容需要进入到OSG中每一帧绘制的代码中,在osg一帧的绘制中会经历 事件遍历、更新遍历、渲染这三个过程,详细代码可以参看每一帧的代码frame函数:

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中,事实上确实如此:
在evnetTraversal处理完场景中事件EventHandler和所有节点的事件回调之后,在函数的最后调用漫游器的事件处理函数:

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中是一个纯虚函数,需要我们实现,它被调用的位置就是在此处。
如何实现自己的漫游器
实现自己的漫游器有多种方式:可以直接从基类osgGA::CameraManipulator开始写,也可以从它的子类如StandardManipulator、DriveManipulator等中继承去写。下面提供一个简单的漫游器示例,这个漫游器实现了当用户使用鼠标滚轮滚动时,视点沿着鼠标位置进行zoom in和zoom out,代码如下:

#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;}
}

————————————————

原文链接:https://blog.csdn.net/csxiaoshui/article/details/51295591


http://www.ppmy.cn/news/496065.html

相关文章

数据库漫游指南

“文艺复兴以降&#xff0c;源远流长的科学精神和逐步形成的学术规范......你们这一脸迷茫的看着我&#xff0c;不知道我在说什么吗&#xff1f;这是机械工业出版社的前言&#xff01;多么经典的书&#xff0c;回去好好看看&#xff01;" 上面这段话来自我本科数据库老师…

802.11漫游详解(一):802.11漫游的历史介绍

802.11漫游详解&#xff08;一&#xff09;&#xff1a;802.11漫游的历史介绍 Wi-Fi网络能快速发展的原因之一是由于802.11技术提供的移动性&#xff08;Mobility&#xff09;&#xff0c;移动性要求客户端具备从一个AP过渡到另一个AP上时仍保持上层应用程序网络连接的功能&am…

移动IP与通信漫游

一直带着一部在江苏办理的合约机在北京上学&#xff0c;遇到过诸多不便。 最近一边复习计算机网络&#xff0c;一边在学TCP/IP协议&#xff0c;正好看到移动IP这一块&#xff0c;理通了心中的疑惑&#xff0c;这里跟大家分享一下。 什么是移动IP&#xff1f; 移动IP技术是移动…

ChatGPT 漫游指南 - 如何快速体验 ChatGPT

根据 OpenAI 开放地区政策[1]&#xff0c;截止到 2023 年 3 月 23 号&#xff0c;ChatGPT 服务 不对中国的大陆、香港和澳门开放&#xff0c;因此大陆的小伙伴们无法直接体验最新的 AI 技术。 即便如此&#xff0c;有很多热心网友搭建了 ChatGPT 镜像网站&#xff0c;即把网站部…

移动主机(无线漫游)和自组织网络

移动主机 例题&#xff1a; 所谓移动IP是指() A.通过地址翻译技术改变主机的IP地址 B.一个主机IP地址可以转移给另一个手机 C.移动主机通过在无线通信网中漫游来保持网络连接 D.移动主机在离开家乡网络的远程站点可以连接工作 答案&#xff1a;C 以下关于无线漫游的说法中&a…

WLAN漫游

漫游&#xff1a;指STA在不同AP覆盖范围之间移动且保持用户业务不中断的行为 WLAN漫游目的&#xff1a; 避免漫游过程中的认证时间过长导致丢包甚至业务中断 保证用户授权信息不变 保证用户IP地址不变 AC内漫游切换过程 &#xff1a; 切换检测&#xff1a;当STA检测到要发生快…

数据可视化漫游(五)

声明&#xff1a;版权所有&#xff0c;转载请联系作者并注明出处 http://blog.csdn.net/u013719780?viewmodecontents 博主简介&#xff1a;风雪夜归子&#xff08;Allen&#xff09;&#xff0c;机器学习算法攻城狮&#xff0c;喜爱钻研Meachine Learning的黑科技&#xff…

数据库系统漫游

演示软件绝大部分为Posrgre,极少数使用mysql&#xff0c;当然查阅书籍可以找到与之相互转化的语句。 数据库mysql环境的配置 安装 2021MySql-8.0.26安装详细教程&#xff08;保姆级&#xff09;_mysql8.0.26_ylb呀的博客-CSDN博客 注意:ini中的安装路径每个人不一样&#x…