利用OSG进行几何球体绘制

一、效果演示

按K键可以进行不同绘制方式的切换

在这里插入图片描述
按K键可以切换为按点绘制

二、逻辑分析

核心逻辑就是顶点数组的计算,设半径长为r,原点为O,球体表面上的任意点P(x, y, z)有x = r * sin(a) * cos(b);y = r * sin(a) * sin(b);z = r * cos(a);其中a为OP向量与z轴的夹角,b为OP向量在xoy平面上的投影与x轴的夹角,以夹角a和夹角b为自变量(-90° <= a <= 90°; 0° <= b <= 360°),循环求得球体表面的点为顶点,如下:

//生成球体的顶点,步长为10°(可以自由调整,但是最好是要能被整除,不然顶点不好对齐)
for(int angleXZ = -90; angleXZ <= 90; angleXZ+=10)
{
	//此处Z轴两级不需要遍历,就两个点,上下各一个
    if(angleXZ == -90 || angleXZ == 90)
    {
        float x = radius*cos(0* M_PI / 180.0)*sin((90-angleXZ)* M_PI / 180.0);
        float y = radius*sin(0* M_PI / 180.0)*sin((90-angleXZ)* M_PI / 180.0);
        float z = radius*cos((90-angleXZ)* M_PI / 180.0);
        pVertexArray->push_back(osg::Vec3f(x, y, z));
        pColorArray->push_back(osg::Vec4(1.0, 0.0, 0.0, 0.5));
    }
    else
    {
        for(int angleXY = 0; angleXY < 360; angleXY+=10)
        {
            float x = radius*cos(angleXY* M_PI / 180.0)*sin((90-angleXZ)* M_PI / 180.0);
            float y = radius*sin(angleXY* M_PI / 180.0)*sin((90-angleXZ)* M_PI / 180.0);
            float z = radius*cos((90-angleXZ)* M_PI / 180.0);
            pVertexArray->push_back(osg::Vec3f(x, y, z));
            pColorArray->push_back(osg::Vec4(1.0, 0.0, 0.0, 0.5));
        }
    }
}

上述将顶点计算出来后,需要按照一定的方式组织绘制,此处需要理解OSG的底层OPenGL的图元装配方式,然后根据其方式的对应特性进行组织顶点的绘制顺序,实在不行也可以亿点点的尝试,头铁,就硬搞,不服来干!如下:

//初始化顶点绘制顺序,其实这个顺序跟你选择的绘制方式有关osg::PrimitiveSet::LINE_STRIP_ADJACENCY,可以玩出各种花儿来
    osg::ref_ptr<osg::DrawElementsUInt>	pDrawEleUInt = new osg::DrawElementsUInt(osg::PrimitiveSet::LINE_STRIP_ADJACENCY);
    //底部
    for(int hI = 0; hI < 36; hI++)
    {
        pDrawEleUInt->push_back(0);
        pDrawEleUInt->push_back(1+hI);
    }
    for(int hI = 0; hI < 36; hI++)
    {
        pDrawEleUInt->push_back(1+hI);
    }
    //中间
    for(int vI = 0; vI < 16; vI++)
    {
        for(int hI = 0; hI < 36; hI++)
        {
            if(hI == 35)
            {
                pDrawEleUInt->push_back(vI*36+hI+1);
                pDrawEleUInt->push_back(vI*36+0+1);
            }
            else
            {
                pDrawEleUInt->push_back(vI*36+hI+1);
                pDrawEleUInt->push_back((vI+1)*36+hI+1);
                pDrawEleUInt->push_back((vI+1)*36+hI+2);
                pDrawEleUInt->push_back(vI*36+hI+2);
            }
        }
    }
    //顶部
    for(int hI = 0; hI < 36; hI++)
    {
        pDrawEleUInt->push_back(17*36+1);
        pDrawEleUInt->push_back(16*36+hI+1);
    }
    for(int hI = 0; hI < 36; hI++)
    {
        pDrawEleUInt->push_back(16*36+hI+1);
    }
    //初始化EBO缓冲,并将上述顶点绘制顺序关联
    osg::ref_ptr<osg::ElementBufferObject> pEboBuffer = new osg::ElementBufferObject;
    pDrawEleUInt->setElementBufferObject(pEboBuffer.get());
    pGeom->addPrimitiveSet(pDrawEleUInt.get());
    pGeom->setUseVertexBufferObjects(true);

三、整体代码实现

#include <iostream>

#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Point>

using namespace std;

/**
 * @brief CreateSphereShape 创建球体形状的几何体
 * @param radius 半径
 * @return osg::ref_ptr<osg::Node> 球体形状节点
 */
osg::ref_ptr<osg::Node> CreateSphereShape(float radius);

//自定义访问器,更换绘制方式的
class CCustomVisitor : public osg::NodeVisitor
{
public:
    CCustomVisitor(){};

    void apply(osg::Geode& node) override
    {
        osg::Drawable* pDrawable = node.getDrawable(0);
        osg::PrimitiveSet* pPrimi = pDrawable->asGeometry()->getPrimitiveSet(0);
        osg::DrawElementsUInt* pDrawE = dynamic_cast<osg::DrawElementsUInt*>(pPrimi);
        if(pDrawE->getMode() == osg::PrimitiveSet::POINTS)
        {
            pDrawE->setMode(osg::PrimitiveSet::LINE_STRIP_ADJACENCY);
        }
        else
        {
            pDrawE->setMode(osg::PrimitiveSet::POINTS);
        }
    }
};

//自定义事件,处理几何模型的
class CCustomEvnet : public osgGA::GUIEventHandler
{
public:
    CCustomEvnet(osg::ref_ptr<osg::Node> pShape)
        : m_pShape(pShape){}

    bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& ee) override
    {
        (void)ee;

        if(ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN
                && ea.getKey() == osgGA::GUIEventAdapter::KEY_K)
        {
            CCustomVisitor visitor;
            m_pShape->accept(visitor);
        }
        return false;
    }

private:
    osg::ref_ptr<osg::Node> m_pShape;
};

int main()
{
    osgViewer::Viewer viewer;
    osg::ref_ptr<osg::Group> pRoot = new osg::Group;

    osg::ref_ptr<osg::Node> pShape = CreateSphereShape(50);


    pRoot->addChild(pShape);
    viewer.setSceneData(pRoot);
    viewer.setUpViewInWindow(100, 100, 800, 600);
    viewer.addEventHandler(new CCustomEvnet(pShape));
    viewer.run();
    return 0;
}

osg::ref_ptr<osg::Node> CreateSphereShape(float radius)
{
    //初始化相关对象
    osg::ref_ptr<osg::Geometry> pGeom = new osg::Geometry;
    osg::ref_ptr<osg::Vec3Array> pVertexArray = new osg::Vec3Array;
    pGeom->setVertexArray(pVertexArray.get());
    osg::ref_ptr<osg::Vec4Array> pColorArray = new osg::Vec4Array;
    pGeom->setColorArray(pColorArray.get());
    pGeom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);

    //生成球体的顶点
    for(int angleXZ = -90; angleXZ <= 90; angleXZ+=10)
    {
        if(angleXZ == -90 || angleXZ == 90)
        {
            float x = radius*cos(0* M_PI / 180.0)*sin((90-angleXZ)* M_PI / 180.0);
            float y = radius*sin(0* M_PI / 180.0)*sin((90-angleXZ)* M_PI / 180.0);
            float z = radius*cos((90-angleXZ)* M_PI / 180.0);
            pVertexArray->push_back(osg::Vec3f(x, y, z));
            pColorArray->push_back(osg::Vec4(1.0, 0.0, 0.0, 0.5));
        }
        else
        {
            for(int angleXY = 0; angleXY < 360; angleXY+=10)
            {
                float x = radius*cos(angleXY* M_PI / 180.0)*sin((90-angleXZ)* M_PI / 180.0);
                float y = radius*sin(angleXY* M_PI / 180.0)*sin((90-angleXZ)* M_PI / 180.0);
                float z = radius*cos((90-angleXZ)* M_PI / 180.0);
                pVertexArray->push_back(osg::Vec3f(x, y, z));
                pColorArray->push_back(osg::Vec4(1.0, 0.0, 0.0, 0.5));
            }
        }
    }

    //初始化顶点绘制顺序,其实这个顺序跟你选择的绘制方式有关osg::PrimitiveSet::LINE_STRIP_ADJACENCY,可以玩出各种花儿来
    osg::ref_ptr<osg::DrawElementsUInt>	pDrawEleUInt = new osg::DrawElementsUInt(osg::PrimitiveSet::LINE_STRIP_ADJACENCY);
    //底部
    for(int hI = 0; hI < 36; hI++)
    {
        pDrawEleUInt->push_back(0);
        pDrawEleUInt->push_back(1+hI);
    }
    for(int hI = 0; hI < 36; hI++)
    {
        pDrawEleUInt->push_back(1+hI);
    }
    //中间
    for(int vI = 0; vI < 16; vI++)
    {
        for(int hI = 0; hI < 36; hI++)
        {
            if(hI == 35)
            {
                pDrawEleUInt->push_back(vI*36+hI+1);
                pDrawEleUInt->push_back(vI*36+0+1);
            }
            else
            {
                pDrawEleUInt->push_back(vI*36+hI+1);
                pDrawEleUInt->push_back((vI+1)*36+hI+1);
                pDrawEleUInt->push_back((vI+1)*36+hI+2);
                pDrawEleUInt->push_back(vI*36+hI+2);
            }
        }
    }
    //顶部
    for(int hI = 0; hI < 36; hI++)
    {
        pDrawEleUInt->push_back(17*36+1);
        pDrawEleUInt->push_back(16*36+hI+1);
    }
    for(int hI = 0; hI < 36; hI++)
    {
        pDrawEleUInt->push_back(16*36+hI+1);
    }
    //初始化EBO缓冲,并将上述顶点绘制顺序关联
    osg::ref_ptr<osg::ElementBufferObject> pEboBuffer = new osg::ElementBufferObject;
    pDrawEleUInt->setElementBufferObject(pEboBuffer.get());
    pGeom->addPrimitiveSet(pDrawEleUInt.get());
    pGeom->setUseVertexBufferObjects(true);

    osg::ref_ptr<osg::Geode> pGeode = new osg::Geode;
    pGeode->addDrawable(pGeom);

    //关闭光照,每个面看起来都一样
    osg::StateSet* stateSet = pGeode->getOrCreateStateSet();
    stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
    stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
    stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
    //设置点的大小
    osg::Point* pointSize = new osg::Point;
    pointSize->setSize(2.0);
    stateSet->setAttribute(pointSize);

    return  pGeode.get();
}
  • 26
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

映月光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值