OSG开发笔记(三十三):同时观察物体不同角度的多视图从相机技术

前言

前面的相机hud可以单独显示图形,继续深入研究相机hud,技术就是子视图了,实现该功能的直接技术是从相机技术。

本篇描述osg从相机技术

Demo


  

相机视口的关键调用

是否清除颜色深度缓存(清除)

cpp 复制代码
pCamera->setClearMask(GL_DEPTH_BUFFER_BIT); 

如果不清除颜色缓存,渲染的窗口中若无内容,则将其他窗口渲染的内容显示到当前窗口。

设置渲染顺序(最后渲染)

cpp 复制代码
// 设置POST渲染顺序(最后渲染)
pCamera->setRenderOrder(osg::Camera::POST_RENDER); 

后渲染的优先级比较高(最后显示,显示优先级最高)。

设置是否接受事件(不接受)

cpp 复制代码
// 设置为不接收事件,始终得不到焦点
pCamera->setAllowEventFocus(false); 

设置视口大小

cpp 复制代码
// 视口就是引擎三维的区域,但是注意区别于屏幕的坐标系(屏幕是左上为0,0,而三维引擎是左下为0,0)
pSlaveFrontCamera->setViewport(0, 0, rect().width() / 4, rect().height() / 4); 

设置从相机故过程

步骤一:新建相机

cpp 复制代码
osg::ref_ptr<osg::Camera> pSlaveFrontCamera = new osg::Camera; 

步骤二:设置上下文

cpp 复制代码
pSlaveFrontCamera->setGraphicsContext(_pViewer->getWindow()); 

步骤三:设置视图区域

cpp 复制代码
// 视口就是引擎三维的区域,但是注意区别于屏幕的坐标系(屏幕是左上为0,0,而三维引擎是左下为0,0)
pSlaveFrontCamera->setViewport(0, 0, rect().width() / 4, rect().height() / 4); 

步骤四:设置渲染顺序

cpp 复制代码
pSlaveFrontCamera->setRenderOrder(osg::Camera::POST_RENDER); 

步骤五:关键步骤添加从相机

第二个参数是缩放矩阵,第三个参数是旋转矩阵

cpp 复制代码
_pViewer->addSlave(pSlaveFrontCamera,
                  osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(0.0), 0.0, 0.0, 0.0), true); 

Demo关键源码

cpp 复制代码
osg::ref_ptr<osg::Node> OsgWidget::getMulViewCameraNode() { // 隐藏整个demo全局的按钮面板(没用到按键的直接隐藏,不影响此Demo) { ui->groupBox_pannel->setVisible(false); ui->label_cursor->setVisible(false); ui->label_cursor_2->setVisible(false); ui->label_msg->setVisible(false); ui->label_state->setVisible(false); } osg::ref_ptr<osg::Group> pGroup = new osg::Group; // 绘制盒体(立方体、长方体) { osg::ref_ptr<osg::Geode> pGeode = new osg::Geode; // 创建专门指明精细度的类osg::TessellationHints,并设置对应精细度 osg::ref_ptr<osg::TessellationHints> pHints = new osg::TessellationHints; pHints->setDetailRatio(0.5); // 绘制几何类型(几何体) qreal width = 5.0f; // 函数1 pGeode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0, 0, 0), width), pHints)); #if 1 // 设置关闭光照:OFF,同时旋转都能看到了(光照关闭,法向量不起作用) { osg::StateSet *pStateSet = pGeode->getOrCreateStateSet(); pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); // pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); } #endif pGroup->addChild(pGeode); } // 创建多视口相机 { #if 0 // 这里改用了自己窗口已经创建的,这块废掉了,但是保留,基本的核心思想是一样的 osg::ref_ptr<osg::GraphicsContext::WindowingSystemInterface> pWindowingSystemInterface = osg::GraphicsContext::getWindowingSystemInterface(); if(!pWindowingSystemInterface.get()) { LOG << "if(!pWindowingSystemInterface.get())"; return pGroup.get(); } unsigned int width = 0; unsigned int height = 0; pWindowingSystemInterface->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), width, height); osg::ref_ptr<osg::GraphicsContext::Traits> pTraits = new osg::GraphicsContext::Traits; { pTraits->x = 0; pTraits->y = 0; pTraits->width = width; pTraits->height = height; pTraits->windowDecoration = false; pTraits->doubleBuffer = true; pTraits->sharedContext = 0; } LOG << pTraits->x << pTraits->y << pTraits->width << pTraits->height; osg::ref_ptr<osg::GraphicsContext> pGraphicsContext = osg::GraphicsContext::createGraphicsContext(pTraits.get()); if(!pGraphicsContext->valid()) { LOG << "if(!pGraphicsContext->valid())"; return pGroup.get(); } #endif double angle = 15.0f; #if 1 // 前 osg::ref_ptr<osg::Camera> pSlaveFrontCamera = new osg::Camera; pSlaveFrontCamera->setGraphicsContext(_pViewer->getWindow()); // 视口就是引擎三维的区域,但是注意区别于屏幕的坐标系(屏幕是左上为0,0,而三维引擎是左下为0,0) pSlaveFrontCamera->setViewport(0, 0, rect().width() / 4, rect().height() / 4); pSlaveFrontCamera->setRenderOrder(osg::Camera::POST_RENDER); _pViewer->addSlave(pSlaveFrontCamera, osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(0.0), 0.0, 0.0, 0.0), true); #endif #if 1 // 后 osg::ref_ptr<osg::Camera> pSlaveBehindCamera = new osg::Camera; pSlaveBehindCamera->setGraphicsContext(_pViewer->getWindow()); // 视口就是引擎三维的区域,但是注意区别于屏幕的坐标系(屏幕是左上为0,0,而三维引擎是左下为0,0) pSlaveBehindCamera->setViewport(0, rect().width() / 4 * 3, rect().width() / 4, rect().height() / 4); pSlaveBehindCamera->setRenderOrder(osg::Camera::POST_RENDER); _pViewer->addSlave(pSlaveBehindCamera, osg::Matrix::translate(0, 0, 0), osg::Matrix::rotate(osg::DegreesToRadians(30), 1.0, 0.0, 0.0), true); #endif #if 0 // 左 // osg::ref_ptr<osg::Camera> pSlaveLeftCamera = new osg::Camera; // pSlaveLeftCamera->setGraphicsContext(_pViewer->getWindow()); osg::ref_ptr<HudRotateCamera> pSlaveLeftCamera = new HudRotateCamera; pSlaveLeftCamera->setGraphicsContext(_pViewer->getWindow()); pSlaveLeftCamera->setMasterCamera(_pViewer->getCamera()); pSlaveLeftCamera->setViewport(0, rect().height() / 8 * 3, rect().width() / 4, rect().height() / 4); pSlaveLeftCamera->setRenderOrder(osg::Camera::POST_RENDER); #if 0 _pViewer->addSlave(pSlaveLeftCamera, osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(angle), 0.0, 0.0, 1.0), true); #endif #if 0 // 设置相机位置,观察目标点和方向 osg::Vec3f vec3Eye = osg::Vec3f(100, 100, 0); osg::Vec3f vec3Center = osg::Vec3f(0, 0, 0); osg::Vec3f vec3Up = osg::Vec3f(0, 1, 0); pSlaveLeftCamera->setViewMatrixAsLookAt(vec3Eye, vec3Center, vec3Up); _pViewer->addSlave(pSlaveLeftCamera); #endif #if 1 // 设置slave相机的位置和方向 osg::ref_ptr<osg::MatrixTransform> transform = new osg::MatrixTransform(); // 设置平移矩阵,根据需要进行调整 osg::Matrix matrix; matrix.makeTranslate(0, 0, 0); // 这里的x, y, z是你想要平移到的位置 transform->setMatrix(matrix); transform->addChild(pGroup); _pViewer->addSlave(pSlaveLeftCamera, osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(15.0f), 0.0, 1.0, 1.0), true); pSlaveLeftCamera->setProjectionMatrixAsPerspective( 100.0f, 1.0, 1.0f, 100.0f); #endif #endif #if 0 // 右 osg::ref_ptr<osg::Camera> pSlaveRightCamera = new osg::Camera; pSlaveRightCamera->setGraphicsContext(_pViewer->getWindow()); pSlaveRightCamera->setViewport(rect().width() / 4 * 3, rect().height() / 8 * 3, rect().width() / 4, rect().height() / 4); pSlaveRightCamera->setRenderOrder(osg::Camera::POST_RENDER); _pViewer->addSlave(pSlaveRightCamera, osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(-angle), 0.0, 0.0, 1.0), true); #endif #if 0 // 上 osg::ref_ptr<osg::Camera> pSlaveUpCamera = new osg::Camera; pSlaveUpCamera->setGraphicsContext(_pViewer->getWindow()); pSlaveUpCamera->setViewport(rect().width() / 8 * 3, 0, rect().width() / 4, rect().height() / 4); pSlaveUpCamera->setRenderOrder(osg::Camera::POST_RENDER); _pViewer->addSlave(pSlaveUpCamera, osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(angle), 1.0, 0.0, 0.0), true); #endif #if 0 // 下 osg::ref_ptr<osg::Camera> pSlaveDownCamera = new osg::Camera; pSlaveDownCamera->setGraphicsContext(_pViewer->getWindow()); pSlaveDownCamera->setViewport(rect().height() / 8 * 3, rect().height() / 8 * 6, rect().width() / 4, rect().height() / 4); pSlaveDownCamera->setRenderOrder(osg::Camera::POST_RENDER); _pViewer->addSlave(pSlaveDownCamera, osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(-angle), 1.0, 0.0, 0.0), true); #endif } return pGroup.get(); } 

工程模板v1.36.0

入坑

入坑一:设置相机就崩溃

问题

解决过程

定位到不设置相机就不崩溃,然后这里是笔者自己造的Qt与OSG结合的,使用了位置,这里也可以查看实际打印的创建的区域坐标和大小,确实也是不对:


  那直接把场景里面的gc赋值给他测试,是可以的,修改的地方有点多,因为这个Qt+OSG是笔者根据源码原理进行调整渲染的,与直接编译出来的qt+osg还是有点区别,总之一句话,就是Qt渲染窗口里面已经有这个osg::ref_ptrosg::GraphicsContext了,不用去额外建立了:

删除以下代码:


  然后再调整相机代码,还有从Qt渲染窗口里面增加拿到这个内容上下文的函数就好了。

解决

新增获取函数,原本不能获取


  
  这里实际大小为:


  所以外面代码,直接用窗口的宽高好了(笔者是铺满的):这里是要缩小放前面,那就是改为4/1吧:


  

入坑二:左视图没有

问题

左视图应该显示,但是没显示

解决过程

改成一样的:


  然后不偏移试试:


  偏移一个小角度试试:


  所以是Y轴的中心不对,但是我们也没有改,测试绕x轴:


  然后绕z轴,发现就z轴没有偏移:


  
  
  尝试单独设置添加相机的视口是无效的:


  
  尝试单独修改同步旋转的相机去修改视口,也是无效:


  继续尝试:


  是否与内置相机的视口有关系,测试也无关:


  

解决(主技术方向未解决)

从原始从相机技术方面暂时没有解决,因为也尝试了更改矩阵、修改相机视角观看位置都没什么变化。

可以确认的是,应该是相机旋转的中心不对,并不是场景中心不对,所以鼠标拽托中间还是在旋转,而视角旋转则x和y轴存在偏移。X和y存在偏移就是左右和里外,若是与屏幕有关也是上下和左右,所以这里这么分析推断也不对。

代码全部放出,读者有兴趣可以提供协助,一起探讨。

规避解决方法

直接在相机中修改偏移旋转,然后当作结点加入,是可以解决,而且还不能是从相机,需要addChild进入:


  这时候拉伸有问题:


  变形了:


  
  终于外挂一个东西解决:


  
  但是鼠标中键按下偏移中心点,会都向右,理论上反向180°的y轴应该向左,但是还是向右,因为是场景偏移,我们规避是对场景下的相机进行旋转,所以实际是移动场景相机了,相机里面的正反对外无效。

官方从相机示例(也存在问题,怀疑是osg3.4.0源码bug)

为了再次深入论证是否代码问题,笔者又用官方的demo实现:

cpp 复制代码
#include <osg/Camera>
#include <osg/Group> #include <osg/Geode> #include <osg/ShapeDrawable> #include <osgViewer/Viewer> #include <osg/GraphicsContext> int main(int argc, char *argv[]) { osg::ref_ptr<osg::Group> pGroup = new osg::Group; // 绘制盒体(立方体、长方体) { osg::ref_ptr<osg::Geode> pGeode = new osg::Geode; // 创建专门指明精细度的类osg::TessellationHints,并设置对应精细度 osg::ref_ptr<osg::TessellationHints> pHints = new osg::TessellationHints; pHints->setDetailRatio(0.5); // 绘制几何类型(几何体) double width = 5.0f; // 函数1 pGeode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0, 0, 0), width), pHints)); // 设置关闭光照:OFF,同时旋转都能看到了(光照关闭,法向量不起作用) { osg::StateSet *pStateSet = pGeode->getOrCreateStateSet(); pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); } pGroup->addChild(pGeode); } osg::GraphicsContext::WindowingSystemInterface * wsi = osg::GraphicsContext::getWindowingSystemInterface(); if(!wsi) { return 0; } osg::ref_ptr<osg::GraphicsContext::Traits > traits = new osg::GraphicsContext::Traits; traits->x = 0; traits->y = 0; traits->width = 800; traits->height = 600; traits->windowDecoration = false; traits->doubleBuffer = true; traits->sharedContext = 0; osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits); if(!gc.valid()) { return 0; } osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer; osg::ref_ptr<osg::Camera> master = new osg::Camera; master->setGraphicsContext(gc); master->setViewport(0, 0, 800, 600); viewer->addSlave(master.get()); osg::ref_ptr<osg::Camera> leftcam = new osg::Camera; leftcam->setGraphicsContext(gc); leftcam->setViewport(0, 0, 800 / 2, 600 / 2); leftcam->setRenderOrder(osg::Camera::POST_RENDER); viewer->addSlave(leftcam.get(), osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(15.0), 0.0, 1.0, 0.0), true); viewer->setSceneData(pGroup); viewer->run(); return 0; } 


  改进后相机代码:

cpp 复制代码
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer; osg::ref_ptr<osg::Camera> master = new osg::Camera; master->setGraphicsContext(gc); master->setViewport(0, 0, 800, 800); viewer->addSlave(master.get()); { osg::ref_ptr<osg::Camera> leftcam = new osg::Camera; leftcam->setGraphicsContext(gc); leftcam->setViewport(0, 0, 100, 100); leftcam->setRenderOrder(osg::Camera::POST_RENDER); viewer->addSlave(leftcam.get(), osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(15.0), 1.0, 0.0, 0.0), true); } { osg::ref_ptr<osg::Camera> leftcam = new osg::Camera; leftcam->setGraphicsContext(gc); leftcam->setViewport(100, 0, 100, 100); leftcam->setRenderOrder(osg::Camera::POST_RENDER); viewer->addSlave(leftcam.get(), osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(15.0), 0.0, 1.0, 0.0), true); } { osg::ref_ptr<osg::Camera> leftcam = new osg::Camera; leftcam->setGraphicsContext(gc); leftcam->setViewport(200, 0, 100, 100); leftcam->setRenderOrder(osg::Camera::POST_RENDER); viewer->addSlave(leftcam.get(), osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(15.0), 0.0, 0.0, 1.0), true); } { osg::ref_ptr<osg::Camera> leftcam = new osg::Camera; leftcam->setGraphicsContext(gc); leftcam->setViewport(300, 0, 100, 100); leftcam->setRenderOrder(osg::Camera::POST_RENDER); viewer->addSlave(leftcam.get(), osg::Matrix(), osg::Matrix::rotate(osg::DegreesToRadians(180.0), 0.0, 0.0, 1.0), true); } 

所以是osg3.4.0的源码这块就有问题: