osgGA::CameraManipulator类computeHomePosition函数分析

osg旋转模型之setViewMatrixAsLookAt()方法和setHomePosition()方法 本人想通过设置camera的位置,实现对模型的旋转操作,最终达到相机在坐标轴的6个不同方向观察模型(Top,bottom,left,right,front,back)。我在此处是将相机在y轴负半轴旋转到Z轴正半轴,以观察模型的Top。下面使用如题的两种方法旋转相机,或许是参数值计算的不对,最后trackBall的中心与boudingSphere的中心完全不同。本文有待更新。 1.首先,获 阅读详情

osgGA::CameraManipulator类computeHomePosition函数代码如下:

void CameraManipulator::computeHomePosition(const osg::Camera *camera, bool useBoundingBox)
{
    if (getNode())
    {
        osg::BoundingSphere boundingSphere;

        OSG_INFO<<" CameraManipulator::computeHomePosition("<<camera<<", "<<useBoundingBox<<")"<<std::endl;

        if (useBoundingBox)
        {
            // compute bounding box
            // (bounding box computes model center more precisely than bounding sphere)
            osg::ComputeBoundsVisitor cbVisitor;
            getNode()->accept(cbVisitor);
            osg::BoundingBox &bb = cbVisitor.getBoundingBox();

            if (bb.valid()) boundingSphere.expandBy(bb);
            else boundingSphere = getNode()->getBound();
        }
        else
        {
            // compute bounding sphere
            boundingSphere = getNode()->getBound();
        }

        OSG_INFO<<"    boundingSphere.center() = ("<<boundingSphere.center()<<")"<<std::endl;
        OSG_INFO<<"    boundingSphere.radius() = "<<boundingSphere.radius()<<std::endl;

        // set dist to default
        double dist = 3.5f * boundingSphere.radius();

        if (camera)
        {

            // try to compute dist from frustum
            double left,right,bottom,top,zNear,zFar;
            if (camera->getProjectionMatrixAsFrustum(left,right,bottom,top,zNear,zFar))
            {
                double vertical2 = fabs(right - left) / zNear / 2.;
                double horizontal2 = fabs(top - bottom) / zNear / 2.;
                double dim = horizontal2 < vertical2 ? horizontal2 : vertical2;
                double viewAngle = atan2(dim,1.);
                dist = boundingSphere.radius() / sin(viewAngle);
            }
            else
            {
                // try to compute dist from ortho
                if (camera->getProjectionMatrixAsOrtho(left,right,bottom,top,zNear,zFar))
                {
                    dist = fabs(zFar - zNear) / 2.;
                }
            }
        }

        // set home position
        setHomePosition(boundingSphere.center() + osg::Vec3d(0.0,-dist,0.0f),
                        boundingSphere.center(),
                        osg::Vec3d(0.0f,0.0f,1.0f),
                        _autoComputeHomePosition);
    }
}

       这个函数用于计算操控器默认位置。该计算方法考虑了相机视场角和模型大小及相机和模型之间的距离足够远,以便能将整个模型投放到计算机屏幕上。如果第1个参数即相机对象为NULL,场景到相机之间的距离将不能被计算,这种情况下将采用默认距离。useBoundingBox参数将用场景的包围盒来代替场景的包围球,因为包围盒在用于计算场景中心时将比包围球更精确,这对某些应用程序来说,有时很重要。

       这个函数中,最难理解的第39到第43行代码,下面分析:

       关于osg的坐标系统有必要解释一下:OpenGL的世界坐标轴向是:x轴向右,y轴向上,z轴向屏幕外。在osg中实际上也是一样的,只不过漫游器在设置视点时把视点设置在了y轴负方向并朝向y轴正向,导致这二者看起来坐标系统不一致。 感觉像是OpenGL坐标系统沿着x轴顺时针翻转90度。并且osg提供的模型数据的顶点坐标也都遵循这一原则,最终让使用者感觉osg的坐标系统是 x轴向右,y轴向屏幕里,z轴朝上。如下为OPenGL中提到的经典的平头截体:

 图1 平头截体

      在上图中,操控器也即相机位于O点;M2点是近面和底面交线的中点(近面和底面是垂直的);M1点是近面和上顶面交线的中点;A点是近平面左上角顶点,其坐标为(left,top);D点近平面右下角顶点,其坐标为(right, bottom);E是左下角顶点,坐标为(left, bottom);OM2 = zNear(注:图中zNear标注的不对); ∠M1OM2 = Foxy为Z轴方向(垂直方向)的视场角;∠EOD = θ是水平方向视场角。
 

 图 2

在图2中,OB线段将垂直方向的视场角等分,即2 * viewAngle = ∠M1OM2 = Foxy, 根据几何关系不难得出:        

   tan(viewAngle) = FB / BO = FB / zNear

     根据图1,FB = (top - bottom) / 2.0,则:

viewAngle = atan2( (top - bottom) / 2.0 / zNear, 1.);

不懂atan2的童鞋可以参考C++中反正切atan2(y,x)与atan(x)的区别 博文。

同样地:

   tan( θ/ 2.0) = EM2 / zNear = (right - left) / 2.0 /zNear

所以:

θ / 2.0 = atan2( (right - left) / 2.0 / zNear, 1.);

为了防止负数,最好加上绝对值,即:

viewAngle = atan2( fab(top - bottom) / 2.0 / zNear, 1.);
θ / 2.0 = atan2( fab(right - left) / 2.0 / zNear, 1.);

      上述的fabs(right - left) / 2 / zNear 就是代码中的vertical2;而fab(top - bottom) / 2.0 / zNear就是代码中的horizontal2。代码中的取名是编写者把变量搞反了,应该是:

double horizontal2 = fabs(right - left) / zNear / 2.;
double vertical2 = fabs(top - bottom) / zNear / 2.;

 这个问题我同gitHub上osg的开发维护者robertosfield 沟通过,下面是他的回复:

沟通连接为:Should naming be exchanged? · Issue #1227 · openscenegraph/OpenSceneGraph (github.com) 。

       然后再比较水平视场角和垂直视场角哪个小,以小的为场景包围球的最终视场角,这样就可以让整个场景被相机观察到,即整个场景都在平头截体内部(大角表示的视场角方向))或被平头截体内切(小角表示的视场角方向)。如果以大角为最终视场角,则当场景正好被大角所表示的平头截体内切时,则此时小的视场角(可能是水平方向的视场角,也可能是垂直方向的视场角)必定和场景包围球相交了,根据OPenGL裁剪原理,所有超出平头截体之外的场景都会被裁剪掉,从而导致部分场景不能显示在计算机屏幕上,这样用户就感觉怪怪的(我想要的场景有部分没显示在屏幕上)。

        算出角度后,根据正弦就很容易算出相机离包围球中心的距离了。最后如果获取相机透视投影参数失败,那么按平行投影进行计算。平行投影的计算比较简单,取远平面和近平面差值的1/2。

参考链接:

【1】:osg中漫游器的原理——osgGA::CameraManipulator(二)。

osg学习(七十)如何根据视口对象自动调整视点位置、距离 osgGA/CameraManipulator.cpp void CameraManipulator::computeHomePosition(const osg::Camera *camera, bool useBoundingBox) { double dist = 3.5f * boundingSphere.radius(); if (camera) { // try to compute dist from frustum ... 阅读详情

相关推荐

osg中默认操作器TrackBall的一点修改--16.12.14

osg中最常用的操作器就是TrackBall,这一操作器用起来体验确实非常棒,但是也存在一些问题,比如最近在osgOcean中使用这一操作器,发现左键操作后海平面一般都变成歪着的,有时想把场景调整“正当“了确实非常费劲。总感觉自己在歪着头看场景,很是别扭。 由于笔者还不太了解此操作器的后台代码,更是对各种矩阵逆矩阵很是头疼,所以想了一个稍微简单的办法解决: osg中场景的显示范

方京的博客 2818

浅谈在操控器类中,为何要通过osgGA::CameraManipulator的逆矩阵改变视点位置

这个变换的过程其实相当于需要求出把B点移回世界坐标原点的矩阵,A点再乘以这个矩阵就可以,这个移回的矩阵是这样计算的,把人从世界坐标原点移到B点的变换是Matrix,而把B点移回世界坐标原点的变换就是InverseMatrix,现在我们要把A点放在以B点为局部坐标下,则需要变换的就是InverseMatrix。原因这里有点绕,大家认真理解一下,世界坐标下,一个盒子在A点(xa,ya,za),视点在B点(xb,yb,zb),先不管朝向不朝向的。虽然入参不一样,但是跟踪一下会发现会调用过去的。

danshiming的博客 548

osg相机(基础篇)

在计算机的三维世界中,相机如同我们的眼睛,捕捉眼前的每一副画面,反过来物体要能够被相机在指定的距离和角度下观看到,并通过屏幕像素绘制出来。 在这个过程中,物体的三维空间坐标需要经过好几层矩阵变换,最终转换到二维平面坐标,并通过像素呈现在屏幕中。我们将这几层矩阵变换过程简称为MVPW矩阵变换。 下面我们展开介绍MVPW具体内容 ~ 模型(Model) MVPW中的M是模型顶点在三维场景中的位置。 一...

lavanana的博客 7192

osg中漫游器的原理——osgGA::CameraManipulator(二)

osgGA库中所有漫游器的基类是CameraManipulator,本文就对这个类进行一个完整的解析。通过之前的分析(详细请参考这一些列的第一篇),可以知道为了实现一个自定义的漫游器,我们至少需要实现以下几个纯虚函数: virtual void setByMatrix(const osg::Matrixd& matrix) = 0; //设置相机的位置姿态矩阵 virtual void setByInverseMatrix(const osg::Matrixd& matri...

whl0071的专栏 2189

六、osg操作器实现

osgGA::CameraManipulator是一个支持自定义相机操作的操作器,支持用户自己定义鼠标、键盘操作来操作相机。常见的自定义鼠标键盘操作需求,比如: 使用键盘控制相机旋转、平移 使用鼠标操作相机旋转、平移 控制场景绕某个视点旋转 第一人称视角的的相机操作 等等

cangqiongxiaoye的博客 1769

中国30米分辨率城市建成区数据集 (1972-2023年)

全国城市建成区空间分布数据(1972-2023年),利用Google Earth Engine平台,基于Landsat系列遥感影像提取了1972–2023年城市不透水面,后经过不透水面聚集密度计算方法计算了不透水面聚集密度,通过阈值划分提取了城市建成区,并设置了建成区面积阈值,去除了噪点,获取了全国城市建成区空间分布数据集。该提取能准确刻画城市形态,提取精度高,保持了建成区的完整性。数据集包括41期数据,1972年,1978年,1985-2023年逐年,分辨率为30米,有栅格和矢量两种格式。

地球资源数据云的官方博客 5824

osgGA::CameraManipulator

上述的fabs(right - left) / 2 / zNear 就是代码中的vertical2;而fab(top - bottom) / 2.0 / zNear就是代码中的horizontal2。沟通连接为:Should naming be exchanged?

hanfenkui的博客 294

机器人动力学仿真软件:Webots_(14).机器人声音仿真

Webots 中的声音仿真主要通过Sound和两种节点来实现。Sound节点用于播放预定义的音频文件,而节点则允许在仿真过程中动态生成和播放声音。这些节点可以用于模拟机器人的语音输出、报警声、环境噪音等。以下是一个示例,展示如何在控制器代码中播放预定义的声音。// 控制器代码(.c) # include <webots/robot.h> # include <webots/sound.h> # define TIME_STEP 64 const char * sound_name = "sound";

kkchenjj的博客 1311

osg中漫游器的原理(二)

osgGA库中所有漫游器的基类是CameraManipulator,本文就对这个类进行一个完整的解析。通过之前的分析(详细请参考这一些列的第一篇),可以知道为了实现一个自定义的漫游器,我们至少需要实现以下几个纯虚函数: virtual void setByMatrix(const osg::Matrixd& matrix) = 0; //设置相机的位置姿态矩阵 virtua...

Jeacean的博客 704

高通8155源码下载与Android源码编译

一.下载代码 高通下载源码位置: Qualcomm CreatePoint 注册账号,然后登入 可以先选择edit -> infotainment -> sa8155 -> favorites, 点击View TOC就可以过滤出sa8155所有的信息目录.如下图 拉取代码链接在ChipCode中,点击进去可以看到所有的基线版本,选择你需要下载的版本点进去,如sa8155p-la-1-0_ap_standard_oem,把高通专有代码和开源代码都下载到本机. 1..

Johnny2004的专栏 7675

泛微OA集成阿里云短信(E9保姆教程)

泛微OA集成阿里云短信(E9保姆教程)

技术分享 515

OSG中获取任一时刻相机的位置

就是在有漫游器的程序中获取相机任一时刻的位置,有时使用漫游器漫游时需要

7344

YOLO26-seg分割优化:卷积魔改 | 动态蛇形卷积(Dynamic Snake Convolution),实现暴力涨点 | ICCV2023

​​​​​​摘要:本研究对Ultralytics YOLO26进行了全面分析,重点阐述了其关键架构改进及其在实时边缘目标检测中的性能基准测试。YOLO26于2025年9月发布,是YOLO系列最新、最先进的成员,专为在边缘及低功耗设备上实现高效、精确且易于部署的目标而构建。本文依次详述了YOLO26的架构创新,包括:移除了分布焦点损失(DFL);采用端到端的无NMS推理;集成了渐进损失(ProgLoss)与小目标感知标签分配(STAL);以及引入了用于稳定收敛的MuSGD优化器。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 217

osg中漫游器的原理(一)

对osg中漫游器的解析,从中可以了解到漫游器的添加、更新、处理和自定义自己漫游器的方式

Frank的专栏 1万+

使用SSC添加大量的SDO条目

使用SSC添加大量的SDO条目 SSC用表格添加SDO,PDO部分必须指定到0x1600和0x1A00分配TxPdo和RxPdo映射,在0x1C12和0x1C13分别分配到SM2和SM3。在0x6000和0x7000未指定的entry就是SDO部分。

PI_sunyang的博客 3166

Python 爬虫实战:手机应用商店评论数据爬取与分析

移动应用市场蓬勃发展,用户的评论数据包含了大量有价值的信息,如用户满意度、功能反馈、改进建议等。通过分析这些评论,我们可以挖掘应用的优缺点,甚至预测用户趋势。由于 Apple App Store 评论是动态加载的,我们使用。本篇博客将详细介绍如何使用 Python 爬取。爬取大量数据时,可能会遇到反爬机制。库获取 Google Play 评论。评论数据是动态加载的,需要使用。

2201_76125261的博客 2322

osg漫游器的原理和执行流程

关键词:manipulator; 漫游器; 第一人称漫游器; osg; osg漫游器的原理; osg漫游器的流程 从CameraManipulator讲起吧,CameraManipulator是漫游器的基类,它继承于GUIEventHandler class OSGGA_EXPORT CameraManipulator : public GUIEventHandler { ... }; ...

三维小糊涂,泽云,三维开发技术分享 1万+

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

osg如何能在程序中改变视点的位置?如何不改变相机的位置来切换操作器

这个问题一直困扰可很久,因为没有认真去看osg源码导致的。 1.在网上收集的资料很多都是 利用osg::carmea 的void setViewMatrixAsLookAt(const osg::Vec3d& eye,const osg::Vec3d& center,const osg::Vec3d& up);这样做能够做到,但是前提是不能使用viewer-》run()。因...

qq_17792451的博客 3309

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

osg视角切换,第一人称和第三人称切换

osg视角切换,第一人称和第三人称切换

WithHair的博客 1139

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

OSG 视景器、漫游器、摄像机与场景的关系

视景器包括几个最主要的组件:漫游器_cameraManipulator,用于实现交互式的场景漫 游;事件处理器组_eventHandlers,负责处理视景器的事件队列_eventQueue,主要是键盘/ 鼠标等事件的处理;场景_scene,它包括视景器所对应的场景图形根节点,以及用于提高节 点和图像数据处理速度的两个分页数据库;摄像机_camera 和_slaves,前者为场景的主摄像 机

ZhangXL的专栏 3825

OSG学习——漫游操作器

在进行制作漫游操作器时,遇到了几个问题。 一:在新的版本下,:MatrixManipulator 已经改名为CameraManipulator。 二:在继承CameraManipulator的类中,四个虚函数 第1个和第2个为设置矩阵及设置逆矩阵,可以不进行设置。 第3个和第4个为得到矩阵和逆矩阵,其中得到逆矩阵和响应函数handle会每一帧都会进行渲染。 三:在响应函数handle...

城南花已开 887

osgGA的CameraManipulator类

操作器基类,用于实现各种操作器接口。本身也是一个操作器的管理类,里面可以压入多个操作器,根据用户按键选择使用哪一个操作器。 virtual const char * className() const 说明:获取当前类的名称 void addMatrixManipulator(int key , std::string nam , CameraManipulator *cm) 说明:添加一个操作器...

Jeacean的博客 1115

osg加载模型后使模型居中且以最佳大小显示

大半年没写博客了,今天再次开写! 问题:单独加载某个模型,像cow.osg加载进去后都是恰好大小的显示在我们前面,但有些我们自己做的模型貌似就不一定了,有时太大,有时太小,甚至有时视野里根本就找不到,但你查看节点又有。这该怎么解决呢? 解决:看到比较多的回答是说用相机回调,更新相机的位置,使他离模型的位置刚好合适。但我想,只是加载的时候用一次来调整相机的位置,需要单独写个相机回调吗?后面在论坛

木头杭的学习博客 6322

qt + osg 根据高程变色之 渐变色

两种色卡,读取点云中的坐标颜色等顶点信息,构建osg::pagelod显示,根据高程VBO动态变色 色卡1 循环高程20 起始高度50 色卡2 void changeColor() { VertexVisitor visitor = VertexVisitor(); m_totalRoot->accept(visitor); // 自定义遍历,获取所有结点xyz最大最小值 double min, max; swi...

阿金的ACM 1524
上一篇: 利用几何画板Sketchpad画球体的方法
下一篇: glBeginQuery/glEndQuery/glQueryCounter获取GPU执行指令耗时
荆楚闲人
博客等级 码龄20年 3万+粉丝 473原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值