osg 八叉树可视化

news2024/10/5 21:25:31

目录

什么是八叉树

八叉树算法过程

八叉树的计算原理

八叉树c++实现

使用osg可视化八叉树


什么是八叉树

在描述三维场景的过程中常常用到一种名为八叉树的数据结构。描述三维空间的八叉树和描述二维空间的四叉树有相似之处,二维空间中正方形可以被分为四个相同形状的正方形,而三维空间中正方体可以被分为八个形状相同的正方体。

八叉树的每个结点表示一个正方体的体积元素,每一个结点有八个子节点,这种用于描述三维空间的树装结构叫做八叉树。

八叉树算法过程

八叉树的计算原理

1. 设定最大递归深度

2. 找出场景的最大尺寸,并以此尺寸建立第一个立方体

3. 依序将单位元元素丢入能包含且没有子节点的立方体

4. 若没达到最大递归深度,就进行细分八等份,再讲该立方体所装的单位元元素全部分组给八个子立方体

5. 若发现子立方体所分配到的单位元元素数量不为零且跟父立方体一样,则该子立方体停止细分

6. 重复3,知道到达最大递归深度

八叉树c++实现

#include <iostream>    
  
using namespace std;    
//定义八叉树节点类    
template<class T>    
struct OctreeNode    
{    
    T data; //节点数据    
    T xmin,xmax; //节点坐标,即六面体个顶点的坐标    
    T ymin,ymax;    
    T zmin,zmax;    
    OctreeNode <T>*top_left_front,*top_left_back; //该节点的个子结点    
    OctreeNode <T>*top_right_front,*top_right_back;    
    OctreeNode <T>*bottom_left_front,*bottom_left_back;    
    OctreeNode <T>*bottom_right_front,*bottom_right_back;    
    OctreeNode //节点类    
        (T nodeValue = T(),    
        T xminValue = T(),T xmaxValue = T(),    
        T yminValue = T(),T ymaxValue = T(),    
        T zminValue = T(),T zmaxValue = T(),    
        OctreeNode<T>*top_left_front_Node = NULL,    
        OctreeNode<T>*top_left_back_Node = NULL,    
        OctreeNode<T>*top_right_front_Node = NULL,    
        OctreeNode<T>*top_right_back_Node = NULL,    
        OctreeNode<T>*bottom_left_front_Node = NULL,    
        OctreeNode<T>*bottom_left_back_Node = NULL,    
        OctreeNode<T>*bottom_right_front_Node = NULL,    
        OctreeNode<T>*bottom_right_back_Node = NULL )    
        :data(nodeValue),    
        xmin(xminValue),xmax(xmaxValue),    
        ymin(yminValue),ymax(ymaxValue),    
        zmin(zminValue),zmax(zmaxValue),    
        top_left_front(top_left_front_Node),    
        top_left_back(top_left_back_Node),    
        top_right_front(top_right_front_Node),    
        top_right_back(top_right_back_Node),    
        bottom_left_front(bottom_left_front_Node),    
        bottom_left_back(bottom_left_back_Node),    
        bottom_right_front(bottom_right_front_Node),    
        bottom_right_back(bottom_right_back_Node){}    
};    
//创建八叉树    
template <class T>    
void createOctree(OctreeNode<T> * &root,int maxdepth,double xmin,double xmax,double ymin,double ymax,double zmin,double zmax)    
{    
    //cout<<"处理中,请稍候……"<<endl;    
    maxdepth=maxdepth-1; //每递归一次就将最大递归深度-1    
    if(maxdepth>=0)    
    {    
        root=new OctreeNode<T>();    
        cout<<"请输入节点值:";    
        //root->data =9;//为节点赋值,可以存储节点信息,如物体可见性。由于是简单实现八叉树功能,简单赋值为9。    
        cin>>root->data;  //为节点赋值    
        root->xmin=xmin; //为节点坐标赋值    
        root->xmax=xmax;    
        root->ymin=ymin;    
        root->ymax=ymax;    
        root->zmin=zmin;    
        root->zmax=zmax;    
        double xm=(xmax-xmin)/2;//计算节点个维度上的半边长    
        double ym=(ymax-ymin)/2;    
        double zm=(ymax-ymin)/2;    
        //递归创建子树,根据每一个节点所处(是几号节点)的位置决定其子结点的坐标。    
        createOctree(root->top_left_front,maxdepth,xmin,xmax-xm,ymax-ym,ymax,zmax-zm,zmax);    
        createOctree(root->top_left_back,maxdepth,xmin,xmax-xm,ymin,ymax-ym,zmax-zm,zmax);    
        createOctree(root->top_right_front,maxdepth,xmax-xm,xmax,ymax-ym,ymax,zmax-zm,zmax);    
        createOctree(root->top_right_back,maxdepth,xmax-xm,xmax,ymin,ymax-ym,zmax-zm,zmax);    
        createOctree(root->bottom_left_front,maxdepth,xmin,xmax-xm,ymax-ym,ymax,zmin,zmax-zm);    
        createOctree(root->bottom_left_back,maxdepth,xmin,xmax-xm,ymin,ymax-ym,zmin,zmax-zm);    
        createOctree(root->bottom_right_front,maxdepth,xmax-xm,xmax,ymax-ym,ymax,zmin,zmax-zm);    
        createOctree(root->bottom_right_back,maxdepth,xmax-xm,xmax,ymin,ymax-ym,zmin,zmax-zm);    
    }    
}    
int i=1;    
//先序遍历八叉树    
template <class T>    
void preOrder( OctreeNode<T> * & p)    
{    
    if(p)    
    {    
        cout<<i<<".当前节点的值为:"<<p->data<<"\n坐标为:";    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        i+=1;    
        cout<<endl;    
        preOrder(p->top_left_front);    
        preOrder(p->top_left_back);    
        preOrder(p->top_right_front);    
        preOrder(p->top_right_back);    
        preOrder(p->bottom_left_front);    
        preOrder(p->bottom_left_back);    
        preOrder(p->bottom_right_front);    
        preOrder(p->bottom_right_back);    
        cout<<endl;    
    }    
}    
//求八叉树的深度    
template<class T>    
int depth(OctreeNode<T> *& p)    
{    
    if(p == NULL)    
        return -1;    
    int h =depth(p->top_left_front);    
    return h+1;    
}    
//计算单位长度,为查找点做准备    
int cal(int num)    
{    
    int result=1;    
    if(1==num)    
        result=1;    
    else    
    {    
        for(int i=1;i<num;i++)    
            result=2*result;    
    }    
    return result;    
}    
//查找点    
int maxdepth=0;    
int times=0;    
static double xmin=0,xmax=0,ymin=0,ymax=0,zmin=0,zmax=0;    
int tmaxdepth=0;    
double txm=1,tym=1,tzm=1;    
template<class T>    
void find(OctreeNode<T> *& p,double x,double y,double z)    
{    
    double xm=(p->xmax-p->xmin)/2;    
    double ym=(p->ymax-p->ymin)/2;    
    double zm=(p->ymax-p->ymin)/2;    
    times++;    
    if(x>xmax || x<xmin|| y>ymax || y<ymin || z>zmax || z<zmin)    
    {    
        cout<<"该点不在场景中!"<<endl;    
        return;    
    }    
    if(x<=p->xmin+txm&& x>=p->xmax-txm && y<=p->ymin+tym &&y>=p->ymax-tym && z<=p->zmin+tzm &&z>=p->zmax-tzm )    
    {    
        cout<<endl<<"找到该点!"<<"该点位于"<<endl;    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        cout<<"节点内!"<<endl;    
        cout<<"共经过"<<times<<"次递归!"<<endl;    
    }    
    else if(x<(p->xmax-xm) && y<(p->ymax-ym) &&z<(p->zmax-zm))    
    {    
        cout<<"当前经过节点坐标:"<<endl;    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        cout<<endl;    
        find(p->bottom_left_back,x,y,z);    
    }    
    else if(x<(p->xmax-xm) && y<(p->ymax-ym) &&z>(p->zmax-zm))    
    {    
        cout<<"当前经过节点坐标:"<<endl;    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        cout<<endl;    
        find(p->top_left_back,x,y,z);    
    }    
    else if(x>(p->xmax-xm) && y<(p->ymax-ym) &&z<(p->zmax-zm))    
    {    
        cout<<"当前经过节点坐标:"<<endl;    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        cout<<endl;    
        find(p->bottom_right_back,x,y,z);    
    }    
    else if(x>(p->xmax-xm) && y<(p->ymax-ym) &&z>(p->zmax-zm))    
    {    
        cout<<"当前经过节点坐标:"<<endl;    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        cout<<endl;    
        find(p->top_right_back,x,y,z);    
    }    
    else if(x<(p->xmax-xm) && y>(p->ymax-ym) &&z<(p->zmax-zm))    
    {    
        cout<<"当前经过节点坐标:"<<endl;    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        cout<<endl;    
        find(p->bottom_left_front,x,y,z);    
    }    
    else if(x<(p->xmax-xm) && y>(p->ymax-ym) &&z>(p->zmax-zm))    
    {    
        cout<<"当前经过节点坐标:"<<endl;    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        cout<<endl;    
        find(p->top_left_front,x,y,z);    
    }    
    else if(x>(p->xmax-xm) && y>(p->ymax-ym) &&z<(p->zmax-zm))    
    {    
        cout<<"当前经过节点坐标:"<<endl;    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        cout<<endl;    
        find(p->bottom_right_front,x,y,z);    
    }    
    else if(x>(p->xmax-xm) && y>(p->ymax-ym) &&z>(p->zmax-zm))    
    {    
        cout<<"当前经过节点坐标:"<<endl;    
        cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
        cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
        cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
        cout<<endl;    
        find(p->top_right_front,x,y,z);    
    }    
}    
//main函数    
int main ()    
{    
    OctreeNode<double> *rootNode = NULL;    
    int choiced = 0;    
    while(true)    
    {    
        system("cls");    
        cout<<"请选择操作:\n";    
        cout<<"1.创建八叉树 2.先序遍历八叉树\n";    
        cout<<"3.查看树深度 4.查找节点   \n";    
        cout<<"0.退出\n\n";    
        cin>>choiced;    
        if(choiced == 0)    
            return 0;    
        else if(choiced == 1)    
        {    
            system("cls");    
            cout<<"请输入最大递归深度:"<<endl;    
            cin>>maxdepth;    
            cout<<"请输入外包盒坐标,顺序如下:xmin,xmax,ymin,ymax,zmin,zmax"<<endl;    
            cin>>xmin>>xmax>>ymin>>ymax>>zmin>>zmax;    
            if(maxdepth>=0|| xmax>xmin || ymax>ymin || zmax>zmin || xmin>0 || ymin>0||zmin>0)    
            {    
                tmaxdepth=cal(maxdepth);    
                txm=(xmax-xmin)/tmaxdepth;    
                tym=(ymax-ymin)/tmaxdepth;    
                tzm=(zmax-zmin)/tmaxdepth;    
                createOctree(rootNode,maxdepth,xmin,xmax,ymin,ymax,zmin,zmax);    
            }    
            else    
            {    
                cout<<"输入错误!";    
                return 0;    
            }    
        }    
        else if(choiced == 2)    
        {    
            system("cls");    
            cout<<"先序遍历八叉树结果:/n";    
            i=1;    
            preOrder(rootNode);    
            cout<<endl;    
            system("pause");    
        }    
        else if(choiced == 3)    
        {    
            system("cls");    
            int dep =depth(rootNode);    
            cout<<"此八叉树的深度为"<<dep+1<<endl;    
            system("pause");    
        }    
        else if(choiced == 4)    
        {    
            system("cls");    
            cout<<"请输入您希望查找的点的坐标,顺序如下:x,y,z\n";    
            double x,y,z;    
            cin>>x>>y>>z;    
            times=0;    
            cout<<endl<<"开始搜寻该点……"<<endl;    
            find(rootNode,x,y,z);    
            system("pause");    
        }    
        else    
        {    
            system("cls");    
            cout<<"\n\n错误选择!\n";    
            system("pause");    
        }    
    }    
}  

使用osg可视化八叉树

 main函数

// osgPro227.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

#include <osg/Group>
#include <osgDB/ReadFile>
#include <osgUtil/PrintVisitor>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制

#include "OctreeBuilder.h"

#pragma comment(lib, "OpenThreadsd.lib")
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgUtild.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgTextd.lib")

float randomValue(float min, float max)
{
    return (min + (float)rand() / (RAND_MAX + 1.0f) * (max - min));
}

osg::Vec3 randomVector(float min, float max)
{
    return osg::Vec3(randomValue(min, max),randomValue(min, max),randomValue(min, max));
}
class PrintNameVisitor : public osgUtil::PrintVisitor
{
public:
    PrintNameVisitor(std::ostream& out) : osgUtil::PrintVisitor(out) {}

    void apply(osg::Node& node)
    {
        if (!node.getName().empty())
        {
            output() << node.getName() << std::endl;
            enter();
            traverse(node);
            leave();
        }
        else osgUtil::PrintVisitor::apply(node);
    }
};

int main(int argc, char** argv)
{
    osg::BoundingBox globalBound;
    std::vector<OctreeBuilder::ElementInfo> globalElements;
    for (unsigned int i = 0; i < 5000; ++i)
    {
        osg::Vec3 pos = randomVector(-500.0f, 500.0f);
        float radius = randomValue(0.5f, 20.0f);
        std::stringstream ss; ss << "Ball-" << i + 1;

        osg::Vec3 min = pos - osg::Vec3(radius, radius, radius);
        osg::Vec3 max = pos + osg::Vec3(radius, radius, radius);
        osg::BoundingBox region(min, max);
        globalBound.expandBy(region);
        globalElements.push_back(OctreeBuilder::ElementInfo(ss.str(), region));
    }

    OctreeBuilder octree;
    osg::ref_ptr<osg::Group> root = octree.build(0, globalBound, globalElements);

    std::ofstream out("octree_output.txt");
    PrintNameVisitor printer(out);
    root->accept(printer);

    osg::ref_ptr <osgViewer::Viewer> viewer = new osgViewer::Viewer;
    viewer->setSceneData(root.get());
	viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
	viewer->addEventHandler(new osgViewer::StatsHandler());//实现状态信息统计
	viewer->addEventHandler(new osgViewer::WindowSizeHandler());

    return viewer->run();
}

OctreeBuilder.h 头文件

#ifndef H_COOKBOOK_CH8_OCTREEBUILDER
#define H_COOKBOOK_CH8_OCTREEBUILDER

#include <osg/Geode>
#include <osg/LOD>

class OctreeBuilder
{
public:
    OctreeBuilder() : _maxChildNumber(16), _maxTreeDepth(8), _maxLevel(0) {}
    int getMaxLevel() const { return _maxLevel; }
    
    void setMaxChildNumber( int max ) { _maxChildNumber = max; }
    int getMaxChildNumber() const { return _maxChildNumber; }
    
    void setMaxTreeDepth( int max ) { _maxTreeDepth = max; }
    int getMaxTreeDepth() const { return _maxTreeDepth; }
    
    typedef std::pair<std::string, osg::BoundingBox> ElementInfo;
    osg::Group* build( int depth, const osg::BoundingBox& total,
                       std::vector<ElementInfo>& elements );
    
protected:
    osg::LOD* createNewLevel( int level, const osg::Vec3& center, float radius );
    osg::Node* createElement( const std::string& id, const osg::Vec3& center, float radius );
    osg::Geode* createBoxForDebug( const osg::Vec3& max, const osg::Vec3& min );
    
    int _maxChildNumber;
    int _maxTreeDepth;
    int _maxLevel;
};

#endif

OctreeBuilder.cpp 

#include <windows.h>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;

#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <osg/PolygonMode>
#include "OctreeBuilder.h"

osg::Group* OctreeBuilder::build( int depth, const osg::BoundingBox& total,std::vector<ElementInfo>& elements )
{
    int s[3];  // axis sides (0 or 1)

    //存放当前包围盒的最大、中间、最小点,以为划分八叉树做准备
    osg::Vec3 extentSet[3] = {
        total._min,
        (total._max + total._min) * 0.5f,
        total._max
    };
    
    std::vector<ElementInfo> childData;
    //遍历父结点的所有孩子,让包含在当前盒子里的,不完全包含但是中点在盒子里的,都压入到当前盒子的子结点
    for ( unsigned int i=0; i<elements.size(); ++i )
    {
        const ElementInfo& obj = elements[i];
        if ( total.contains(obj.second._min) && total.contains(obj.second._max) )
            childData.push_back( obj );
        else if ( total.intersects(obj.second) )
        {
            osg::Vec3 center = (obj.second._max + obj.second._min) * 0.5f;
            if (total.contains(center))
            {
                childData.push_back(obj);
            }
        }
    }
    
    //如果当前结点的孩子数量已经达标,或者层数已经达标,则认为是叶结点
    bool isLeafNode = false;
    if ((int)childData.size() <= _maxChildNumber || depth > _maxTreeDepth)
    {
        isLeafNode = true;
    }
    
    //当前八叉树根
    osg::ref_ptr<osg::Group> group = new osg::Group;

    //如果不是叶结点,继续分,把空间一分为八
    if ( !isLeafNode )
    {
        osg::ref_ptr<osg::Group> childNodes[8];

        //空间一分为八2*2*2
        for ( s[0]=0; s[0]<2; ++s[0] ) //x
        {
            for ( s[1]=0; s[1]<2; ++s[1] ) //y
            {
                for ( s[2]=0; s[2]<2; ++s[2] ) //z
                {
                    // Calculate the child extent
                    //extentSet 0是最小,1是中间,2是最大
                    //下面这个小算法有点磨人,分别求出min和max的x, y, z自己好好推几个试试
                    osg::Vec3 min, max;
                    for ( int a=0; a<3; ++a )
                    {
                        min[a] = (extentSet[s[a] + 0])[a];
                        max[a] = (extentSet[s[a] + 1])[a];
                    }
                    
                    //这么求id是为了确保唯一性
                    int id = s[0] + (2 * s[1]) + (4 * s[2]);
                    childNodes[id] = build( depth+1, osg::BoundingBox(min, max), childData );
                }
            }
        }
        
        //八个子结点构建完毕后,加入到根结点当中
        for ( unsigned int i=0; i<8; ++i )
        {
            if ( childNodes[i] && childNodes[i]->getNumChildren() )
                group->addChild( childNodes[i] );
        }
    }
    else //找到叶结点,递归就结束了
    {
        for ( unsigned int i=0; i<childData.size(); ++i )
        {
            const ElementInfo& obj = childData[i];
            osg::Vec3 center = (obj.second._max + obj.second._min) * 0.5;
            float radius = (obj.second._max - obj.second._min).length() * 0.5f;
            //创建一个球
            group->addChild( createElement(obj.first, center, radius) );
        }
    }
    
    osg::Vec3 center = (total._max + total._min) * 0.5;
    float radius = (total._max - total._min).length() * 0.5f;

    //最后创建一个LOD,离的远了显示调试盒子,离的近了显示分的组
    osg::LOD* level = createNewLevel( depth, center, radius );
    level->insertChild( 0, createBoxForDebug(total._max, total._min) );  // For debug use
    level->insertChild( 1, group.get() );
    return level;
}

osg::LOD* OctreeBuilder::createNewLevel( int level, const osg::Vec3& center, float radius )
{
    osg::ref_ptr<osg::LOD> lod = new osg::LOD;
    lod->setCenterMode( osg::LOD::USER_DEFINED_CENTER );
    lod->setCenter( center );
    lod->setRadius( radius );
    lod->setRange( 0, radius * 5.0f, FLT_MAX );
    lod->setRange( 1, 0.0f, radius * 5.0f );
    
    if ( _maxLevel<level ) _maxLevel = level;
    return lod.release();
}

osg::Node* OctreeBuilder::createElement( const std::string& id, const osg::Vec3& center, float radius )
{
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable( new osg::ShapeDrawable(new osg::Sphere(center, radius)) );
    geode->setName( id );
    return geode.release();
}

osg::Geode* OctreeBuilder::createBoxForDebug( const osg::Vec3& max, const osg::Vec3& min )
{
    osg::Vec3 dir = max - min;
    osg::ref_ptr<osg::Vec3Array> va = new osg::Vec3Array(10);
    (*va)[0] = min + osg::Vec3(0.0f, 0.0f, 0.0f);
    (*va)[1] = min + osg::Vec3(0.0f, 0.0f, dir[2]);
    (*va)[2] = min + osg::Vec3(dir[0], 0.0f, 0.0f);
    (*va)[3] = min + osg::Vec3(dir[0], 0.0f, dir[2]);
    (*va)[4] = min + osg::Vec3(dir[0], dir[1], 0.0f);
    (*va)[5] = min + osg::Vec3(dir[0], dir[1], dir[2]);
    (*va)[6] = min + osg::Vec3(0.0f, dir[1], 0.0f);
    (*va)[7] = min + osg::Vec3(0.0f, dir[1], dir[2]);
    (*va)[8] = min + osg::Vec3(0.0f, 0.0f, 0.0f);
    (*va)[9] = min + osg::Vec3(0.0f, 0.0f, dir[2]);
    
    osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
    geom->setVertexArray( va.get() );
    geom->addPrimitiveSet( new osg::DrawArrays(GL_QUAD_STRIP, 0, 10) );
    
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable( geom.get() );
    geode->getOrCreateStateSet()->setAttribute(
        new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE) );
    geode->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
    return geode.release();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1187364.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3.29每日一题(微分方程的几何应用题:重点考察)

1、画图&#xff0c;把题目中的条件标出来 2、通过题目中的条件设出正确的微分方程&#xff08;解题的关键&#xff09; 注&#xff1a;用点斜式设方程的时候&#xff0c;注意Y - y y&#xff08;X - x&#xff09;中&#xff08;x&#xff0c;y&#xff09;为曲边上的动点&a…

srs webrtc推拉流环境搭建

官方代码https://github.com/ossrs/srs 拉取代码&#xff1a; git clone https://github.com/ossrs/srs.gitcd ./configure make ./objs/srs -c conf/rtc.confconf/rtc.conf中&#xff0c;当推拉流浏览器在本地时&#xff0c;如果srs也在本地&#xff0c;那么可以使用官网默认…

C++ STL - map 与 multimap用法和区别

目录 一、概述 二、用法 2.1、插入 2.2、拷贝与赋值 2.3、查找 2.4、删除 2.5、完整代码 三、其他成员类型 一、概述 map 与 multimap是存储key-value&#xff08;键-值 对&#xff09;类型的容器。不同之处在于&#xff1a;map只允许key与 value一一对应&#xff1b;…

在医疗影像领域,生成式模型可以做些什么?用什么平台快速实现?使用MONAI框架进行生成式模型开发

生成模型具有巨大的潜力&#xff0c;不仅有助于通过合成数据集安全地共享医疗数据&#xff0c;还可以执行一系列逆向应用&#xff0c;如异常检测、图像到图像翻译、去噪和MRI重建。然而&#xff0c;由于这些模型的复杂性&#xff0c;它们的实现和再现性可能很困难。 对于我这种…

图像分割项目中损失函数的选择

文章目录 前言场景&#xff1a;实际项目中&#xff0c;通常会有一个常见的问题&#xff1a;样本不均衡一、focal loss思考 二、Dice loss三、二分类未完待续 前言 在图像分割领域&#xff0c;最基础、最常见的损失当然是交叉熵损失 —— Cross entropy。随着不断的研究&#x…

一个java文件的JVM之旅

准备 我是小C同学编写得一个java文件&#xff0c;如何实现我的功能呢&#xff1f;需要去JVM(Java Virtual Machine)这个地方旅行。 变身 我高高兴兴的来到JVM&#xff0c;想要开始JVM之旅&#xff0c;它确说&#xff1a;“现在的我还不能进去&#xff0c;需要做一次转换&#x…

OceanBase 如何通过日志观测冻结转储流程?

本文旨在通过日志解析 OceanBase 的冻结转储流程&#xff0c;以其冻结检查线程为切入点&#xff0c;以租户&#xff08;1002&#xff09;的线程名为例。 作者&#xff1a;陈慧明&#xff0c;爱可生测试工程师&#xff0c;主要参与 DMP 和 DBLE 自动化测试项目。 爱可生开源社区…

2258. 逃离火灾 : 详解如何从「二分」到「分类讨论」(图解过程)

题目描述 这是 LeetCode 上的 「2258. 逃离火灾」 &#xff0c;难度为 「困难」。 Tag : 「多源 BFS」、「二分」、「预处理」 给你一个下标从 0 开始大小为 m x n 的二维整数数组 grid&#xff0c;它表示一个网格图。 每个格子为下面 个值之一&#xff1a; 0 表示草地。 1 表…

【狂神说Java】SpringSecurity+shiro

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;【狂神说Java】 &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0c…

ShuffleNet系列 网络结构

文章目录 ShuffleNet V1Channel Shuffle&#xff1a;通道打散SuffleNet UnitModel Architecture实验结果 ShuffleNet V2Guideline 1Guideline 2Guideline 3Guideline 4模型结构代码 论文&#xff1a;ShuffleNet: ShuffleNet: An Extremely Efficient Convolutional Neural Netw…

2023年的低代码:数字化、人工智能、趋势及未来展望

本文由葡萄城技术团队发布。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 正如许多专家预测的那样&#xff0c;低代码平台在2023年将展现更加强劲的势头。越来越多的企业正在纷纷转向低代…

ArcGIS 气象风场等示例 数据制作、服务发布及前端加载

1. 原始数据为多维数据 以nc数据为例。 首先在pro中需要以多维数据的方式去添加多维数据&#xff0c;这里的数据包含uv方向&#xff1a; 加载进pro的效果&#xff1a; 这里注意 数据属性需要为矢量uv&#xff1a; 如果要发布为服务&#xff0c;需要导出存储为tif格式&…

spring 中 @Validated/@Valid

超级好的链接 添加链接描述

Vue实现面经基础版案例(路由+组件缓存)

一、面经基础版-案例效果分析 1.面经效果演示 2.功能分析 通过演示效果发现&#xff0c;主要的功能页面有两个&#xff0c;一个是列表页&#xff0c;一个是详情页&#xff0c;并且在列表页点击时可以跳转到详情页底部导航可以来回切换&#xff0c;并且切换时&#xff0c;只有…

AI:69-基于深度学习的音乐推荐

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

JavaScript脚本操作CSS

脚本化CSS就是使用JavaScript脚本操作CSS&#xff0c;配合HTML5、Ajax、jQuery等技术&#xff0c;可以设计出细腻、逼真的页面特效和交互行为&#xff0c;提升用户体验&#xff0c;如网页对象的显示/隐藏、定位、变形、运动等动态样式。 1、CSS脚本化基础 CSS样式有两种形式&…

OpenCV 在ImShow窗体上选择感兴趣的区域

窗体上选择感兴趣ROI区域 在计算机视觉处理中, 通常是针对图像中的一个特定区域进行处理, 有时候这个特定区域需要人来选择, OpenCV 也提供了窗口选择ROI机制. 窗体支持两种选择ROI区域的方法, 一个是单选, 一个是多选, 操作方法如下: 单选: 通过鼠标在屏幕上选择区域, 然后通过…

【Linux系统编程十六】:(基础IO3)--用户级缓冲区

【Linux系统编程十六】&#xff1a;基础IO3--用户级缓冲区 一.用户级缓冲区二.缓冲区刷新策略1.验证&#xff1a; 三.缓冲区意义 一.用户级缓冲区 我们首先理解上面的代码&#xff0c;分别使用printf和fprintf&#xff0c;fwrite往1号文件描述符里输出&#xff0c;也就是往显示…

论文阅读——InternImage(cvpr2023)

arxiv&#xff1a;https://arxiv.org/abs/2211.05778 github&#xff1a;https://github.com/OpenGVLab/InternImage 一、介绍 大部分大模型都是基于transformer的&#xff0c;本文是一个基于CNN的视觉基础模型。使用可变性卷积deformable convolution作为核心操作&…