查看: 4490|回复: 30

请教大家问题:求复杂场景中某个物体的包围盒并显示(Array帮忙看下,用了您的一个方法)

[复制链接]

该用户从未签到

发表于 2010-11-4 22:48:23 | 显示全部楼层 |阅读模式
我的做试验的场景是这样的:
场景图.jpg
    PAT1~3都是些空间变换的结点,node1~node3分别是一个三角锥、一个圆柱和那头牛。
    “画出的包围盒结点”是一个自己定义的画包围盒的函数的返回值,函数头是这样子的:
osg::ref_ptr<osg::Geode> createBoundingBox(osg::BoundingBox &box,osg::Vec3f nodeCenter)

    还有一段重要的代码是(可能看代码更描述的清楚):
osg::ComputeBoundsVisitor boundvisitor;


PAT3->accept(boundvisitor);
//
现在画的是 Node3的包围盒,node3是那头牛


osg::BoundingBox bb;


bb = boundvisitor.getBoundingBox();


osg::Vec3f nodeCenter;


nodeCenter=bb.center()*osg::computeLocalToWorld((PAT3->getParentalNodePaths()[0]));
    root->addChild(createBoundingBox(bb,nodeCenter));//就是把那个画出来的方框给添加到根节点显示。

    显示结果如效果图1所示:(这个是在预料中的,因为我只要画node3,也就是那头牛的包围盒)
效果图1.jpg

    现在我想画整个这三个结点的包围盒,我把代码改成这样:
osg::ComputeBoundsVisitor boundvisitor;


PAT1->accept(boundvisitor);
//
现在画的是整个的包围盒,因为PAT1是在最上面的


osg::BoundingBox bb;


bb = boundvisitor.getBoundingBox();


osg::Vec3f nodeCenter;


nodeCenter=bb.center()*osg::computeLocalToWorld((PAT1->getParentalNodePaths()[0]));
    root->addChild(createBoundingBox(bb,nodeCenter));//就是把那个画出来的方框给添加到根节点显示。

    效果图2是我的第二个试验的结果:
效果图2.jpg
    这个结果明显就不对了,因为我需要的是将这三个node都包含在内的包围盒。
    这个结果给我的感觉是,包围盒确实在我所需要的空间点生成了,并且大小也是正确的,好像没有进行位置的平移一样。
    上面的那个代码是参考array在一个帖子里面说的方式做的,我试验了,但有问题,所以特在此来问问。哪位大侠知道错误的原因在哪边么?该怎么修改呢?谢谢

该用户从未签到

 楼主| 发表于 2010-11-4 22:49:34 | 显示全部楼层
如果哪位大侠知道,群里面说一下也可以的哇

该用户从未签到

发表于 2010-11-5 08:58:21 | 显示全部楼层
您可以去掉nodeCenter=bb.center()*osg::computeLocalToWorld((PAT1->getParentalNodePaths()[0]));
中的*osg::computeLocalToWorld((PAT1->getParentalNodePaths()[0]));
试试。。

该用户从未签到

发表于 2010-11-5 09:00:24 | 显示全部楼层
注意一点:ComputeBoundsVisitor给出的结果永远是在世界坐标系下的!也就是根节点的坐标系,因此您这里的“bb.center()*osg::computeLocalToWorld()”是错误的。第一次计算pat3的结果也并不正确,正确的包围盒应该完全包裹住牛的模型,而您绘制的包围盒显然经过了多余的变换

该用户从未签到

发表于 2010-11-5 17:03:57 | 显示全部楼层
试验了一下,
如果不对PAT1,PAT2,PAT3做旋转操作,用
osg::ComputeBoundsVisitor boundvisitor;
node1->accept(boundvisitor);         
osg::BoundingBox bb;
bb = boundvisitor.getBoundingBox();
osg::Vec3f nodeCenter;
nodeCenter=bb.center();
root->addChild(createBoundingBox(bb,nodeCenter));
对node1,node2,node3求包围盒完全正确。

而如果对三个PAT分别用 PAT1->setAttitude(osg:uat(osg:egreesToRadians(45.0),osg::Vec3(0,1,0)));
等做旋转,之后在该PAT子节点求包围盒,全部都有错误,位置不对。到底该如何解决?恳请解答,多谢各位老师

该用户从未签到

发表于 2010-11-5 19:25:30 | 显示全部楼层
问题补充:
PAT1没有旋转时,[img]file:///F:/pic1.jpg [/img]
PAT1旋转45度,PAT1->setAttitude(osg:uat(osg:egreesToRadians(45.0),osg::Vec3(0,1,0))); 之后为[img]
file:///F:/pic2.jpg :
[/img]  ,node1包围盒还是按原来的位置计算,没有重新计算

该用户从未签到

发表于 2010-11-5 19:36:46 | 显示全部楼层
PAT1没有旋转时, pic1.jpg
PAT1旋转45度,PAT1->setAttitude(osguat(osgegreesToRadians(45.0),osg::Vec3(0,1,0))); 之后为
pic2.jpg
node1包围盒还是按原来的位置计算,没有变化,不知为何

该用户从未签到

 楼主| 发表于 2010-11-6 10:07:39 | 显示全部楼层
按照我对您说的话的意思的理解以及结合3楼朋友给出的建议,我将代码做了如下的改动,也就是说使得bb是以根的坐标系为基准的[localimg=143,150]1[/localimg]。代码如下:
  osg::ComputeBoundsVisitor boundvisitor;
  PAT2->accept(boundvisitor);  //也就是画出牛和圆柱的那个包围盒
  osg::BoundingBox bb;
  bb = boundvisitor.getBoundingBox();
  root->addChild(createBoundingBox(bb,bb.center));
但是这样做试验的结果并不对,这个包围盒给我的感觉还是没有跟随当前节点在世界坐标系中的位置的变化而变化,依旧在原始位置进行绘制,即bb.center这个值并没有进行PAT变换。请教一下,这可能是哪里弄错了呢?

我把变换的代码也给上:
PAT2->setPosition(PAT2->getPosition()+osg::Vec3(30,0,0));
  PAT2->setAttitude(osg:uat(osg:egreesToRadians(45.0),osg::Vec3(0,1,1)));
     PAT3->setAttitude(osg::Quat(osg::DegreesToRadians(45.0),osg::Vec3(0,1,1)));
  PAT3->setPosition(PAT3->getPosition()+osg::Vec3(30,0,0));
错误图(牛+圆柱).jpg

[localimg=143,150]1[/localimg]
[localimg=143,150]1[/localimg][localimg=143,150]1[/localimg]

该用户从未签到

发表于 2010-11-6 12:54:46 | 显示全部楼层
root->addChild(createBoundingBox(bb,bb.center));
这段代码改为PAT1->addChild(createBoundingBox(bb,bb.center));

该用户从未签到

发表于 2010-11-6 12:55:00 | 显示全部楼层
good luck。。

该用户从未签到

 楼主| 发表于 2010-11-6 19:06:24 | 显示全部楼层
oh~~ 还是不对的。。。哎

该用户从未签到

发表于 2010-11-7 14:50:35 | 显示全部楼层
生成包围盒的目的是为了碰撞检测,当然应该是在同一坐标系下,将所有的包围盒都放在root才正确啊,如果像9楼说的把每个物体包围盒分别放在自己的PAT下,那各物体的包围盒没有一个统一的坐标系,碰撞检测如何做啊?正是因为这方面的考虑才想寻求该问题的解决办法哦

该用户从未签到

发表于 2010-11-8 09:17:59 | 显示全部楼层
。。不知道您是遇到了什么问题了。。我做包围盒的时候貌似没什么问题。。。其思想是center*localToWorldMatrix localToWorldMatirx就是osg::computelocaltoWorld计算出来的,然后连入根节点即可。。。

该用户从未签到

发表于 2010-11-8 09:24:36 | 显示全部楼层
您有没有考虑过,是否是您的createBoundingBox函数不正确?

这里附上我写的包围体求取并绘制的代码:
  1. osg::ComputeBoundsVisitor cbbv;
  2. node->accept( cbbv );

  3. osg::BoundingBox bb = cbbv.getBoundingBox();
  4. boxNode->setMatrix(
  5.     osg::Matrix::scale(bb.xMax()-bb.xMin(), bb.yMax()-bb.yMin(), bb.zMax()-bb.zMin()) *
  6.     osg::Matrix::translate(bb.center()) );
复制代码

其中boxNode是一个MatrixTransform(内含一个ShapeDrawable-Box,Box的中心始终为0,半径始终为1),它必须放置在根节点下

该用户从未签到

 楼主| 发表于 2010-11-8 14:52:57 | 显示全部楼层

RE: 求复杂场景中某个物体的包围盒并显示(Array帮忙看下,按照您的方式又重新改过了)

我担心模型上面会对我要的效果有影响,所以我重新构建了3个非常简单的结点(球、立方体和圆柱);同时我把那个createBoundBox函数取消了,直接放到main中来做了。这样场景图如下:
场景图.jpg
    如果不画包围盒的话,整个场景显示如下图:
无包围盒.jpg
    按照14楼array老师给的建议,我改动了代码后,用PAT2这个结点进行了测试,预期效果是该包围盒包含进正方体geode2和圆柱geode3,但是我的结果不对,错误的效果图如下所示:
错误的效果图.jpg
    我这两天一直在弄这个,也搞不清楚为什么不可以,所以把源码贴上吧,另外,我把工程直接上传上来,直接就可以运行看出错误的地方。如果您有什么改动的好建议,恳请指教,非常谢谢各位老师的指导了。

#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Shape>
#include <osg/ShapeDrawable>
#include <osg/MatrixTransform>
#include <osg/ComputeBoundsVisitor>
#include <osgGA/TrackballManipulator>
#include <osg/PositionAttitudeTransform>
#include <osg/BoundingBox>
#include <osg/PolygonMode>
#include <osg/LineWidth>

int main()
{
        osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
        osg::ref_ptr<osg::Group> root = new osg::Group;//最大的那个根节点
        
        osg::ref_ptr<osg::Geode> geode1 =new osg::Geode;//定义了三个叶节点
        osg::ref_ptr<osg::Geode> geode2 =new osg::Geode;
        osg::ref_ptr<osg::Geode> geode3 =new osg::Geode;

    osg::TessellationHints *hints=new osg::TessellationHints;//设置画出的模型的精确程度
        hints->setDetailRatio(0.5);

        geode1->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f,0.0f,0.0f),0.08),hints));//绘制一个球,中心在原点
        geode2->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,0.0f,0.0f),0.16),hints));//绘制一个立方体,中心在原点
        geode3->addDrawable(new osg::ShapeDrawable(new osg::Cylinder(osg::Vec3(0.0f,0.0f,0.0f),0.08,0.1),hints));//绘制一个圆柱,中心在原点
        
    osg::ref_ptr<osg:ositionAttitudeTransform> PAT1=new osg::PositionAttitudeTransform;//定义了三个空间变换节点
        osg::ref_ptr<osg::PositionAttitudeTransform> PAT2=new osg::PositionAttitudeTransform;
        osg::ref_ptr<osg::PositionAttitudeTransform> PAT3=new osg::PositionAttitudeTransform;
        PAT1->setAttitude(osg:uat(osg:egreesToRadians(45.0),osg::Vec3(0,1,1)));//为了让三个模型散开便于观察,对他们的位置进行变换
        PAT2->setPosition(osg::Vec3(0.2,0,0));
        PAT2->setAttitude(osg::Quat(osg::DegreesToRadians(45.0),osg::Vec3(0,1,1)));
    PAT3->setAttitude(osg::Quat(osg::DegreesToRadians(45.0),osg::Vec3(0,1,1)));
        PAT3->setPosition(osg::Vec3(0.2,0,0));

        //这一段代码构建场景,如图所示
        root->addChild(PAT1);
        PAT1->addChild(geode1);
        PAT1->addChild(PAT2);
        PAT2->addChild(geode2);
        PAT2->addChild(PAT3);
        PAT3->addChild(geode3);
   
        //创建一个box,只画出盒子的边缘线,将这个box挂在一个osg::MatrixTransform boxMT节点上
        osg::ref_ptr<osg::Geode> box = new osg::Geode;
        osg::ref_ptr<osg::ShapeDrawable> sd = new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,0.0f,0.0f),1.0),hints);
    sd->setColor(osg::Vec4(1,0,0,0.2));
        osg::ref_ptr<osg::StateSet> state = sd->getOrCreateStateSet();
        osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode:INE);
    state->setAttributeAndModes(pm.get());
    osg::ref_ptr<osg::LineWidth> lw = new osg::LineWidth(3.0f);
    state->setAttribute(lw.get());
        box->addDrawable(sd.get());

        osg::ref_ptr<osg::MatrixTransform> boxMT=new osg::MatrixTransform;//将那个box挂到boxMT上
        boxMT->addChild(box);        
   
        //计算包围盒
        osg::ComputeBoundsVisitor boundvisitor;
        PAT2->accept(boundvisitor);//这里是计算PAT2节点的包围盒,预期效果是包含进正方体geode2和圆柱geode3
        osg::BoundingBox bb;
        bb = boundvisitor.getBoundingBox();
    boxMT->setMatrix(osg::Matrix::scale(bb.xMax()-bb.xMin(), bb.yMax()-bb.yMin(), bb.zMax()-bb.zMin()) *osg::Matrix::translate(bb.center()) );
    root->addChild(boxMT);//把这个boxMT挂在了根root上面,具体如图所示

        viewer->setCameraManipulator(new osgGA::TrackballManipulator);//漫游
    viewer->setSceneData(root.get());
    while (!viewer->done())
      {
            viewer->frame();
      }

        return 0;
}

该用户从未签到

 楼主| 发表于 2010-11-8 14:55:58 | 显示全部楼层
整个工程上传比较难,我把cpp文件上传吧,就一个,很容易。运行的时候就添一些动态链接库就行了。

arrayPAT.rar

1.36 KB, 下载次数: 163, 下载积分: 威望 1

该用户从未签到

 楼主| 发表于 2010-11-8 15:12:16 | 显示全部楼层
谢谢各位老师的帮忙~~

该用户从未签到

发表于 2010-11-8 16:10:11 | 显示全部楼层
经过一番测试,问题得到解决,可能不是很优化的办法,但至少可以实现我想要的功能了
1。要求node1节点的包围盒,需要
PAT2->setNodeMask(0); //先将其他多余节点隐藏掉
...
PAT1->accept(boundvisitor);//再求包围盒
...
PAT2->setNodeMask(1);  //最后将被隐藏掉的节点显示
2。 如果求node2节点的包围盒,分两种情况,如果PAT1发生过节点变化(旋转等操作),则必须先隐藏掉node1节点,用PAT1->accept(boundvisitor); 求出来即为node2包围盒
如果PAT1没有发生过节点变化,则可以直接用PAT2->accept(boundvisitor);
同理,可以求node3节点的包围盒。
总之,必须从第一个变化了的PAT开始求。
这种办法隐藏显示也比较麻烦,不知道高手还有没有更好的办法

该用户从未签到

发表于 2010-11-8 16:59:00 | 显示全部楼层
我在14楼的方法您是否看到和测试过了?我的工程都使用的这个办法,从未出过问题

该用户从未签到

 楼主| 发表于 2010-11-8 17:52:12 | 显示全部楼层
我就是按照您的意思改动的呀:
    osg::ComputeBoundsVisitor boundvisitor;
    PAT2->accept(boundvisitor);//这里是计算PAT2节点的包围盒,预期效果是包含进正方体geode2和圆柱geode3
    osg::BoundingBox bb;
    bb = boundvisitor.getBoundingBox();
    boxMT->setMatrix(osg::Matrix::scale(bb.xMax()-bb.xMin(), bb.yMax()-bb.yMin(), bb.zMax()-bb.zMin()) *osg::Matrix::translate(bb.center()) );
    root->addChild(boxMT);//把这个boxMT挂在了根root上面,具体如图所示

那个boxMT是这么定义的:
  osg::ref_ptr<osg::MatrixTransform> boxMT=new osg::MatrixTransform;
  boxMT->addChild(box);  

其中box是一个geode结点,接了一个drawable正方体,且中心在(0,0,0),边长为1.

该用户从未签到

 楼主| 发表于 2010-11-8 17:59:45 | 显示全部楼层
我看实验的结果给我的感觉是这样的:
那个用osg::ComputeBoundsVisitor boundvisitor来计算包围盒,返回的并不是根节点所在目录的坐标系,而是它的父节点的坐标系。
在我的这个场景中,PAT2的父节点PAT1也进行了变换,所以最后出来的结果是包围盒在显示的时候以PAT1处理后的坐标系为基准的,而不是ROOT结点所在的坐标系。这个原因导致了盒子的位置不对。
我试验了一下,如果将这个盒子也经过PAT1所采用的平移、旋转、缩放等操作后再显示,那样就对了。
我现在想搞明白的就是,怎么做才可以不管他的父节点发生了何种空间变换,直接返回该包围盒在整个root所在的坐标系中的位置。

该用户从未签到

发表于 2010-11-8 21:29:55 | 显示全部楼层
您好。。看出您的问题了。。。计算出来的包围盒是某一局部坐标系下的包围盒。。您需要做的是osg::computeLocalToWorld函数。。计算出来这个节点父矩阵。。。然后bb.center*osg::computeLocalToWorld()所得到的矩阵。。

该用户从未签到

发表于 2010-11-8 21:31:48 | 显示全部楼层
您的代码我也看了。。不过我想上面的回答可以帮您解决问题了。。如果还不行。。建议您看些矩阵变换的相关知识。。

该用户从未签到

发表于 2010-11-9 08:35:46 | 显示全部楼层
ComputeBoundsVisitor的结果就是世界坐标系下的,这一点毋庸置疑!如果您不相信,可以参考它的源代码,其中很明确地使用了computeLocalToWorldMatrix()!!
我现在不能判断到底是您自己的代码存在问题,还是PAT节点的问题;因为我从来都是使用MatrixTransform的。您可以将自己所有的PAT换成MatrixTransform,然后看看还会不会产生不正确的计算结果

该用户从未签到

 楼主| 发表于 2010-11-9 09:03:16 | 显示全部楼层
恩,好的,我去试试看。谢谢老师这么不厌其烦的帮助我~~

该用户从未签到

 楼主| 发表于 2010-11-9 09:04:06 | 显示全部楼层
3Q~~我去试试看,总是打扰您,非常抱歉~~谢谢

该用户从未签到

 楼主| 发表于 2010-11-9 09:52:36 | 显示全部楼层
我按照您的意思,把我所有的PAT结点都换成了MatrixTransform节点,原来的场景图结构不变。用类似于如下的三条语句分别对mt1\mt2\mt3进行矩阵设置,让这些形体进行空间位置的变换:
mt1->setMatrix(osg::Matrix::rotate(45,osg::Vec3f(0,1,1))*osg::Matrix::translate(osg::Vec3f(0.9,0.0,0.0)));

    然后:
    root->addChild(mt1);
    mt1->addChild(geode1);//geode1是geode结点,下挂shapedrawable的一个球
    。。。。

    紧接着我又创建包围盒的那个盒子结点:
    osg::ref_ptr<osg::Geode> box = new osg::Geode;
osg::ref_ptr<osg::ShapeDrawable> sd = new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,0.0f,0.0f),1),hints);//盒子在原点,半径为1
    box->addDrawable(sd.get());

    然后我我又建了一个MatrixTransform的boxMT,让上面的box挂在它下面,即:
    boxMT->addChild(box);

    接下来的就是使用您教我的方法写的了,跟您的那段代码基本一致:
    //计算包围盒
osg::ComputeBoundsVisitor boundvisitor;
mt3->accept(boundvisitor);//计算mt3节点的包围盒,应该包含进圆柱geode3
osg::BoundingBox bb;
bb = boundvisitor.getBoundingBox();
boxMT->setMatrix(osg::Matrix::scale(bb.xMax()-bb.xMin(), bb.yMax()-bb.yMin(), bb.zMax()-bb.zMin()) *osg::Matrix::translate(bb.center()) );
    root->addChild(boxMT);//把这个boxMT挂在根root上面,具体如上面的场景图

    然后就是渲染,程序就没了。但是结果还是不对,错误的效果图是:
    错误效果图.jpg

    其实我都不好意思总是打扰您了,只是弄不对,心里面总有疙瘩。非常感谢您,真的。又不知道我哪边写的有问题。。。

该用户从未签到

 楼主| 发表于 2010-11-9 09:54:59 | 显示全部楼层
array很确定地告诉我,用
ComputeBoundsVisitor的结果就是世界坐标系下的
,因此如果再用bb.center*computeLocalToWorld的话肯定进行了多余的矩阵变换。我也按照您的方式试验了下,结果确实不对,虽然我不知道为什么。。。。

该用户从未签到

发表于 2010-11-9 13:02:27 | 显示全部楼层
既然MatrixTransform也不对,那我可以肯定是您的代码本身存在问题了~~

该用户从未签到

 楼主| 发表于 2010-11-9 13:48:34 | 显示全部楼层
好吧,尽管还是没搞明白自个儿错在哪儿,但还是要谢谢您。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表