三、视景器osgViewer::Viewer :osgViewer::View,osgViewer::ViewerBase\仿真循环
相机节点实现了三维世界向二维世界窗口变换的问题,图形设备确立了相机拍摄内容与某个图形窗口或像素设备的对应关系,那么视景器就是高效地将一个或多个相机或多个图形设备组织在一起,并结合场景节点的交互、更新、裁剪与遍历,将相机所在的每一帧所拍摄的内容连续地播放出来(仿真循环)。
1.视景器主要工作
2.视图osgViewer::View,必须使用ref_ptr
设置一个视景器的视图时,至少需要:
- 所观察的场景节点树(根节点)
- 所使用的漫游器工具,以及附加的交互事件处理器(有默认的)
- 场景的主相机,如果没有设置,那么会使用一个默认的camera节点,视图类会自动将场景树追加到这个相机节点下。
osg::ref_ptr<osgViewer::View> view = new osgViewer::View;
view->setCameraManipulator...
view->setEventHandler...
view->setSceneData...
3.单视景器和多视景器
- 单视景器/单视图一般只保存一颗完整的场景节点树,并且通常只在一个视图View中浏览和管理。
- 多视景器保存多颗完整的场景节点树。
- 一棵树可以被多个视图对象同时引用,意味着可以从不同角度观察同一个物体。
- 多个视图意味着可以分别浏览不同的场景,共用或者独享一个图形窗口,以及拥有自己的全局渲染状态等。
单视景器osgViewer::Viewer
其实就是实现了ViewerBase和View的纯虚函数。
构建与View相同,最后可以使用run()
运行仿真循环,并且//Also calls realize() if the viewer is not already realized, and installs trackball //manipulator if one is not already assigned.。
- 但是也可以自行编写仿真循环:执行frame()
while (!viewer.done())
viewer.frame();
多视景器osgViewer::CompositeViewer
新增了少量成员函数,用于更新和维护内部的各个视图对象,也是通过多视图来实现多视景器的。
可能会共享同一个窗口,也可能会打开多个窗口。渲染都是通过frame统一管理。
单视景器/单视图与相机
单视图的管理通过osgViewer::Viewer实现。
- osg::View主要用来管理所有相机视图,包含一个主相机和N个从属相机Slave。如果只有一个主相机,则该主相机用来负责控制和渲染视图场景;如果包含从属相机,则主相机负责控制管理视图,从属负责渲染场景。
- osgViewer::View可以挂节事件,处理事件,并负责创建相机和创建图形环境窗口。
- osgViewer::ViewBase具有管理渲染的线程,负责设置线程模式,启动相关线程等功能。
- osgGA::GUIActionAdapte是GUI动作适配器,用来向系统发送一些请求。
在一个osgViewer::Viewer只允许单视图,但是同时包含多个相机渲染,也可以在窗口中渲染。
而osgViewer::Viewer可以多视图。
实例:自定义相机和图形设备
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"));
// 设置图形环境特性
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits();
traits->x = 0;//左上角,且根据电脑屏幕来的!
traits->y = 0;
traits->width = 1920;
traits->height = 1080;
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->sharedContext = 0;
// 创建图形环境特性
osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
if(gc.valid())
{
gc->setClearColor(osg::Vec4f(0.2f,0.2f,0.6f,1.0f));
gc->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
// 设置视口,用于最后变换标准化设备坐标,x和y是相对与Traits中定义的窗口的左下角开始的偏移。
viewer->getCamera()->setViewport(new osg::Viewport(0,0,traits->width,traits->height));// 使用默认相机
// 设置图形环境
viewer->getCamera()->setGraphicsContext(gc.get());
viewer->setSceneData(group.get());
return viewer->run();
}
实例:单视图多相机渲染:四个辅助相机拼接,投影墙技术(RELATIVE_RF),单窗口
osg::Camera* createCamera(int x, int y, int w, int h)
{
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
traits->x = x;//左上角,且根据电脑屏幕来的!
traits->y = y;
traits->width = w;
traits->height = h;
traits->doubleBuffer = true;
//osg::DisplaySetting* ds = osg::DisplaySetting::instance();
osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
osg::ref_ptr<osg::Camera> camera = new osg::Camera;
camera->setGraphicsContext(gc.get());
//设置视口,用于最后变换标准化设备坐标,x和y是相对与Traits中定义的窗口的左下角开始的偏移。
camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
return camera.release();
}
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
// 参数二设置对于同一个场景节点树,挪动相机位置。因为同一个视景器多相机共享主相机的VP矩阵,需要适当偏移设置。
viewer->addSlave(createCamera(100,100,400, 300), osg::Matrixd::translate(1.0, -1.0, 0.0), osg::Matrixd());
viewer->addSlave(createCamera(505,100,400,300), osg::Matrixd::translate(-1.0, -1.0, 0.0), osg::Matrixd());
viewer->addSlave(createCamera(100,405,400,300), osg::Matrixd::translate(1.0, 1.0, 0.0), osg::Matrixd());
viewer->addSlave(createCamera(505,405,400,300), osg::Matrixd::translate(-1.0, 1.0, 0.0), osg::Matrixd());
osg::ref_ptr<osg::Group> group = new osg::Group();
group->addChild(osgDB::readNodeFile("cow.osg"));
viewer->setSceneData(group.get());
return viewer->run();
}
实例:多视图(对应一个相机)+单窗口:多个操作器,可以只旋转其中一个对象+根据分辨率来确定合适的投影来保证显示的图形不变形
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/CompositeViewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/Camera>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgGA/TrackballManipulator>
#include <osgUtil/Optimizer>
#include <iostream>
int main()
{
//创建CompositeViewer对象
osg::ref_ptr<osgViewer::CompositeViewer> viewer = new osgViewer::CompositeViewer();
//读取牛的模型
osg::ref_ptr<osg::Node> cow = osgDB::readNodeFile("cow.osg");
//读取飞机模型
osg::ref_ptr<osg::Node> cessna = osgDB::readNodeFile("cow.osg");
//优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(cow.get());
optimizer.optimize(cessna.get());
//设置图形环境特性
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits();
traits->x = 100;
traits->y = 100;
traits->width = 900;
traits->height = 700;
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->sharedContext = 0;
//创建图形环境
osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
if (gc->valid())
{
osg::notify(osg::INFO) << " GraphicsWindow has been created successfully." << std::endl;
//清除窗口颜色及清除颜色和深度缓冲
gc->setClearColor(osg::Vec4f(0.2f, 0.2f, 0.6f, 1.0f));
gc->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
else
{
osg::notify(osg::NOTICE) << " GraphicsWindow has not been created successfully." << std::endl;
}
//视图一
{
//创建视图一
osg::ref_ptr<osgViewer::View> view = new osgViewer::View;
viewer->addView(view.get());
//设置视图场景数据
view->setSceneData(cow.get());
//设置相机视口及图形环境
view->getCamera()->setViewport(new osg::Viewport(0, 0, traits->width / 2, traits->height / 2));
view->getCamera()->setGraphicsContext(gc.get());
//设置操作器
view->setCameraManipulator(new osgGA::TrackballManipulator);
//添加事件处理
view->addEventHandler(new osgViewer::StatsHandler);
view->addEventHandler(new osgViewer::WindowSizeHandler);
view->addEventHandler(new osgViewer::ThreadingHandler);
view->addEventHandler(new osgViewer::RecordCameraPathHandler);
}
//视图二
{
osg::ref_ptr<osgViewer::View> view = new osgViewer::View;
viewer->addView(view.get());
view->setSceneData(cessna.get());
view->getCamera()->setViewport(new osg::Viewport(traits->width / 2, 0, traits->width / 2, traits->height / 2));
view->getCamera()->setGraphicsContext(gc.get());
view->setCameraManipulator(new osgGA::TrackballManipulator);
}
//视图三
{
osg::ref_ptr<osgViewer::View> view = new osgViewer::View;
//viewer->addView(view.get());
view->setSceneData(cessna.get());
//根据分辨率来确定合适的投影来保证显示的图形不变形
double fovy, aspectRatio, zNear, zFar;
view->getCamera()->getProjectionMatrixAsPerspective(fovy, aspectRatio, zNear, zFar);
double newAspectRatio = double(traits->width) / double(traits->height / 2);
double aspectRatioChange = newAspectRatio / aspectRatio;
if (aspectRatioChange != 1.0)
{
view->getCamera()->getProjectionMatrix() *= osg::Matrix::scale(1.0 / aspectRatioChange, 1.0, 1.0);
}
view->getCamera()->setViewport(new osg::Viewport(0, traits->height / 2, traits->width, traits->height / 2));
view->getCamera()->setGraphicsContext(gc.get());
view->setCameraManipulator(new osgGA::TrackballManipulator);
}
viewer->realize();
viewer->run();
return 0;
}
实例:多视景器/多视图和多窗口渲染:多个场景节点树1
int main(int argc, char** argv)
{
osg::ref_ptr<osgViewer::View> view1 = new osgViewer::View;
view1->setUpViewInWindow(0, 50, 320, 240);
view1->setSceneData(osgDB::readNodeFile("cow.osg"));
osg::ref_ptr<osgViewer::View> view2 = new osgViewer::View;
view2->setUpViewInWindow(320, 50, 320, 240);
view2->setSceneData(osgDB::readNodeFile("cessna.osg"));
osg::ref_ptr<osgViewer::View> view3 = new osgViewer::View;
view3->setUpViewInWindow(640, 50, 320, 240);
view3->setSceneData(osgDB::readNodeFile("axes.osg"));
osgViewer::CompositeViewer compositeViewer;
compositeViewer.addView(view1.get());
compositeViewer.addView(view2.get());
compositeViewer.addView(view3.get());
return compositeViewer.run();
}
实例:多视景器/多视图和多窗口渲染:多个场景节点树2
从osgViewer::ViewerBase可知,创建一个新的GC,就是创建一个新的窗口。
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/Camera>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <iostream>
int main()
{
//创建一个CompositeViewer对象
osg::ref_ptr<osgViewer::CompositeViewer> viewer = new osgViewer::CompositeViewer();
//创建两个视图
osg::ref_ptr<osgViewer::View> viewer1 = new osgViewer::View();
osg::ref_ptr<osgViewer::View> viewer2 = new osgViewer::View();
int xoffset = 50;
int yoffset = 200;
//视图一的节点
osg::ref_ptr<osg::Node> viewer1Node = osgDB::readNodeFile("cow.osg");
//视图二的节点
osg::ref_ptr<osg::Node> viewer2Node = osgDB::readNodeFile("cow.osg");
//优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(viewer1Node.get());
optimizer.optimize(viewer2Node.get());
//视图窗口一
{
//设置图形环境特性
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits();
traits->x = xoffset + 0;
traits->y = yoffset + 0;
traits->width = 600;
traits->height = 480;
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->sharedContext = 0;
//创建图形环境
osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
//创建相机
osg::ref_ptr<osg::Camera> camera1 = viewer1->getCamera();
//设置图形环境
camera1->setGraphicsContext(gc.get());
//设置视口
camera1->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
//设置缓冲
GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
camera1->setDrawBuffer(buffer);
camera1->setReadBuffer(buffer);
//设置场景数据
viewer1->setSceneData(viewer1Node.get());
}
//视图窗口二
{
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits();
traits->x = xoffset + 600;
traits->y = yoffset + 0;
traits->width = 600;
traits->height = 480;
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->sharedContext = 0;
osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
osg::ref_ptr<osg::Camera> camera2 = viewer2->getCamera();
camera2->setGraphicsContext(gc.get());
camera2->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
camera2->setDrawBuffer(buffer);
camera2->setReadBuffer(buffer);
viewer2->setSceneData(viewer2Node.get());
}
//添加视图
viewer->addView(viewer1.get());
viewer->addView(viewer2.get());
viewer->realize();
viewer->run();
return 0;
}