OSG繪製空間凹多邊形並計算其面積

1. 思路

這個問題其實涉及到OSG中的兩個問題:多邊形分格化和幾何圖元遍歷。ios

1) 多邊形分格化

在OpenGL/OSG中,因爲效率的緣由,默認是直接顯示的簡單的凸多邊形。若是直接強行顯示凹多邊形,渲染結果是不肯定的。因此對於複雜的凹多邊形,須要將其分解成簡單的凸多邊形,這個過程就是多邊形分格化。在OSG中是經過osgUtil::Tessellator類來實現多邊形分格化的。數組

2) 幾何圖元遍歷

對於二維的凹多邊形,能夠有辦法計算其面積。可是對於三維空間的凹多邊形,計算其面積卻很困難。這是由於三維空間凹多邊形甚至都有可能不是共面的。而咱們知道,任何複雜的圖形都是經過分解成三角形進行繪製的,只要獲取分解成的三角形,計算其面積並相加(空間三角形的面積計算比較簡單),就能夠獲得凹多邊形的總面積。
在OSG中提供了一個用來訪問圖元的類:osg::PrimitiveFunctor,其繼承類osg::TriangleFunctor能夠獲取其三角面圖元。幾何體類osg::Geometry提供了遍歷幾何圖元的訪問器接口。ide

2. 實現

其具體實現以下。注意在查找多邊形分格化的資料的時候,提到了環繞數和環繞規則的概念。在OSG裏面也有相應的參數設置。惋惜這一段沒有看明白,只能根據仿照例子來設置了。學習

#include <iostream>
#include <Windows.h>

#include <osgViewer/Viewer>
#include <osgDB/ReadFile>

#include <osgUtil/Tessellator>
#include <osg/TriangleFunctor>

using namespace std;
using namespace osg;

osg::ref_ptr<osg::Geometry> redPolygon;

//計算空間三角形的面積
double CalTriangleArea(const osg::Vec3& a, const osg::Vec3& b, const osg::Vec3& c)
{
    double area = 0;

    double side[3];//存儲三條邊的長度;

    side[0] = sqrt(pow(a.x() - b.x(), 2) + pow(a.y() - b.y(), 2) + pow(a.z() - b.z(), 2));
    side[1] = sqrt(pow(a.x() - c.x(), 2) + pow(a.y() - c.y(), 2) + pow(a.z() - c.z(), 2));
    side[2] = sqrt(pow(c.x() - b.x(), 2) + pow(c.y() - b.y(), 2) + pow(c.z() - b.z(), 2));

    //不能構成三角形;
    if (side[0] + side[1] <= side[2] || side[0] + side[2] <= side[1] || side[1] + side[2] <= side[0]) return area;

    //利用海倫公式。s=sqr(p*(p-a)(p-b)(p-c));
    double p = (side[0] + side[1] + side[2]) / 2; //半周長;
    area = sqrt(p*(p - side[0])*(p - side[1])*(p - side[2]));

    return area;
}

//三角面片訪問器
struct TriangleAreaFunctor
{
    TriangleAreaFunctor()
    {
        sumArea = new double;
    }

    ~TriangleAreaFunctor()
    {
        if (sumArea)
        {
            delete sumArea;
            sumArea = nullptr;
        }
    }

    void operator() (const osg::Vec3& v1, const osg::Vec3& v2, const osg::Vec3& v3) const
    {
        *sumArea = *sumArea + CalTriangleArea(v1, v2, v3);
    }

    double GetSumArea()
    {
        return *sumArea;
    }

protected:
    double *sumArea = nullptr;
};


//
ref_ptr<Geode> createPolygon()
{
    const float wall[6][3] =
    {
        { -115.54f, 70.873f, -118.952f},
        { -111.516f, 70.7189f, -71.8492f },
        { -88.5345f, 70.8667f, -86.3565f },
        { -64.9495f, 71.8231f, -53.6525f },
        { -52.9755f, 69.028f, -129.093f },
        { -89.2272f, 71.1478f, -105.434f }
    };

    //
    ref_ptr<Geode> geode = new Geode();
    redPolygon = new osg::Geometry;
    
    //
    osg::ref_ptr<osg::Vec3Array> redVex = new osg::Vec3Array;
    redPolygon->setVertexArray(redVex);

    for (int i = 0; i< 6; i++)
    {       
        redVex->push_back(osg::Vec3(wall[i][0], wall[i][1], wall[i][2]));
    }
    
    redPolygon->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, 6));

    //設置顏色數組
    osg::ref_ptr<osg::Vec4Array> redColors = new osg::Vec4Array;
    redColors->push_back(osg::Vec4(1.0, 0.0, 0.0, 0.5));
    redPolygon->setColorArray(redColors);
    redColors->setBinding(osg::Array::BIND_PER_PRIMITIVE_SET);

    //若是須要透明,則加入這個
    redPolygon->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
    redPolygon->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
    
    //建立分格化對象(支持凹多邊形)
    osg::ref_ptr<osgUtil::Tessellator> tscx = new osgUtil::Tessellator;
    //設置分格類型爲幾何體
    tscx->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
    //設置只顯示輪廓線爲false。設置環繞規則,這裏不太懂
    tscx->setWindingType(osgUtil::Tessellator::TESS_WINDING_ODD);
    //使用分格化
    tscx->retessellatePolygons(*(redPolygon.get()));
    
    geode->addDrawable(redPolygon);

    return geode;
}

int main()
{
    //
    ref_ptr<Group> root = new Group();
    root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);        //關閉默認光照
    root->addChild(createPolygon());

    //
    osgViewer::Viewer viewer;
    viewer.setSceneData(root);
    viewer.setUpViewInWindow(100, 100, 800, 600);
    viewer.run();

    osg::TriangleFunctor<TriangleAreaFunctor> tf;
    redPolygon->accept(tf);
    cout << "面積:" << tf.GetSumArea() << endl;

    return 0;
}

渲染結果以下:
ui

3. 參考

  1. OSG學習筆記(三)之如何將非三角面轉換爲三角面
  2. osg幾何體的圖元的遍歷
  3. OSG計算並繪製模型中每個三角面片的法向量
  4. OSG(OpenSceneGraph)基礎學習9:OSG多邊形分格化
相關文章
相關標籤/搜索