zoukankan      html  css  js  c++  java
  • osg三维重建的两种方法剖析:三角面片(osgUtil::DelaunayTriangulator)和四角面片(osg::HeightField) (1)

    最近项目中需要利用osg重建三维曲面,所以学习了一下。

    第一,我先用的狄洛尼三角形的方法,即osgUtil::DelaunayTriangulator,用这种方法的特点是:

    1.首先必须给其一个存储三维点集的数组,该方法会对这些杂乱无章的散点自动排序,然后就利用这些排好序的,符合三角网构建规则的散点去构建三角网,需要注意的是经过dt->setInputPointArray(coords);这句话以后,数组coords的值的顺序已经发生改变,不再是原来的coords。

    2.再给其贴纹理的时候,必须要首先设置一个颜色数组给它,需要注意的是,纹理的坐标都是0~1范围的,而且是二维的(x,y),所以必须将coords的坐标的x和y值一 一 映射到0~1的范围。

    3.必须输出法向量,并用该法向量数组给对应的geometry赋值

    具体代码如下:

    //创建Delaunay三角网对象
     osg::ref_ptr<osgUtil::DelaunayTriangulator> dt = new osgUtil::DelaunayTriangulator();
     dt->setInputPointArray(coords);//赋给它三维点集数组
     dt->setOutputNormalArray(normals);//输出法向量
     //生成三角网
     dt->triangulate();
     osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
     //设置坐标
     geometry->setVertexArray(coords.get());
     //设置描述
     geometry->addPrimitiveSet(dt->getTriangles());
     //设置法线
     geometry->setNormalArray(normals.get());
     geometry->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
     //设置纹理坐标(纹理填充)
     osg::ref_ptr<osg::Vec2Array> texCoords = ComputerTextureCoords(*(coords.get()));//得到一 一映射后的范围在0~1的二维纹理数组
     geometry->setTexCoordArray(0,texCoords.get());

    //尝试颜色填充
    //  osg::ref_ptr<osg::Vec4Array> vextexColorArray = ComputePerVertexColor(*(coords.get()),getOSGColorTable());
    //  geometry->setColorArray(vextexColorArray );
    //  geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);

    //准备纹理图像

    //生成一副QImage,给其每个像素用事先定义好的颜色赋值。最后保存成png图像,保存方式就是image.save(strPath)参数是保存全文件名,包括路径和后缀。

    QImage image(xCount,yCount,QImage::Format_Indexed8);
      QVector<QRgb> colorTable = getColorTable();
     image.setColorTable(colorTable); 
     InterpolateAndDrawImage(vecZs,&image,xCount,yCount,xCount,yCount); 
     QString strName = ::GetImagePath() + pContourData->GetName() +".png";
     image.save(strName);//保存成png图像

    //开始用png图像生成纹理
      osg::ref_ptr<osg::Image> texImage = osgDB::readImageFile(strName.toStdString());

     osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D;
     tex->setImage(texImage.get());
     tex->setDataVariance(osg::Object::DYNAMIC);

     osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
     stateset->setTextureAttributeAndModes(0,tex.get(),osg::StateAttribute::ON);


     osg::ref_ptr<osg::Geode> geode = new osg::Geode();
     geode->addDrawable(geometry.get());
     geode->setStateSet(stateset.get());

     //设置矩阵变换矩阵
     m_pRootSwitch->addChild(geode);

    这样利用三角面片重建三维曲面,并且给其贴纹理渲染的效果就已经出来了,但是需要注意的是如果点集是规则网格数据,这种方式的构建就不适合了,应该用四角面片osg::HeightField的方式。

    二、四边形面片

    这种方式构建的特点如下:

    1必须给其分配一个行列号,即构建出的网格有多少行多少列,利用allocate(unsigned int rownum,unsigned int columnnum)函数来分配。

    2.必须给其指定一个初始位置,用setOrigin((const osg::Vec3& origin)来指定,注意三维顶点数组的z轴设为0即可,点集合中最小的x、y即可。

    3.必须给其指定x和y方向每行每列之间的间隔用setXInterval(float dx );setYInterval(float dy)来指定。

    4.有了初始位置,行列号,和间隔,就可以算出四边形面片每个顶点的位置了,然后在每个位置上设置高程值即可,用setHeight(float height)

    具体实现过程如下,纹理渲染过程与三角面片类似,只是少了一个步骤就是:不需要给其设置纹理坐标,跟简单容易。

    osg::ref_ptr<osg::HeightField> pHeightField = new osg::HeightField();
     pHeightField->allocate(xCount,yCount);
     pHeightField->setOrigin(osg::Vec3(xMin,yMin,0));
     pHeightField->setXInterval( xdelta );
     pHeightField->setYInterval( ydelta );
     
     float x,y;
     for(int i = 0; i < yCount; i++)
     {
      for(int j = 0; j < xCount; j++)
      {
       x = xMin + j * xdelta;
       y = yMin + i * ydelta;
       double z = pInterpolater->GetInterpolatedZ(x,y,input.begin(),input.end());
       vecZs.push_back(-z+zMin);
       coords->push_back(GeoToGeoNormal(osg::Vec3f(x,y,0)));
       
       pHeightField->setHeight(j,yCount-i-1,-z);//循环得到每个顶点,然后为其设置z值
      }
     }

    ///绘制纹理图像
     QImage image(xCount,yCount,QImage::Format_Indexed8);
      QVector<QRgb> colorTable = getColorTable();
     image.setColorTable(colorTable); 
     InterpolateAndDrawImage(vecZs,&image,xCount,yCount,xCount,yCount); 
     QString strName = ::GetImagePath() + pContourData->GetName() +".png";
     image.save(strName);
      osg::ref_ptr<osg::Image> texImage = osgDB::readImageFile(strName.toStdString());

     osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D;
     tex->setImage(texImage.get());
     tex->setDataVariance(osg::Object::DYNAMIC);

     osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
     stateset->setTextureAttributeAndModes(0,tex.get(),osg::StateAttribute::ON);

    osg::ref_ptr<osg::Geode> geode = new osg::Geode();
     geode->addDrawable(new osg::ShapeDrawable(pHeightField.get()));
     geode->setStateSet(stateset.get());

     

     

    //附录:贴纹理用到的三个函数:

    第一个将真实顶点坐标一 一映射到(0~1)的范围

    osg::ref_ptr<osg::Vec2Array>  COSG3DSurfaceNode::ComputerTextureCoords( const osg::Vec3Array & vp)
    {
     osg::ref_ptr<osg::Vec2Array> texCoords = new osg::Vec2Array();
     int nSize = vp.size();
     
     float maxX = vp[0].x();
     float minX = maxX;
     float maxY = vp[0].y();
     float minY = maxY;

     for (int i=0;i<nSize;i++)
     {
      maxX = maxX<vp[i].x()?vp[i].x():maxX;
      minX = minX>vp[i].x()?vp[i].x():minX;
      maxY = maxY<vp[i].y()?vp[i].y():maxY;
      minY = minY>vp[i].y()?vp[i].y():minY;
     }

     for(int i = 0;i< nSize; i++)
     {
      float xValue = 1-(maxX-vp[i].x())/(maxX - minX);
      //float yValue = 1-(maxY-vp[i].y())/(maxY - minY);
      float yValue = 1-(vp[i].y()-minY)/(maxY - minY);
      texCoords->push_back(osg::Vec2(xValue,yValue));
     }
     
     return texCoords;
    }

    第二,由颜色配置文件得到颜色数组

    QVector<QRgb> COSG3DSurfaceNode::getColorTable()
    {
     QVector<QRgb> table;

     QString colotpath = ::GetImagePath() + "colorbar.txt";
     QFile file(colotpath);
     if (!file.open(QIODevice::ReadOnly))
     {
      assert(false);
     }

     QTextStream WXStream(&file);
     for (int i=0;i<32;i++)
     {  
      float RColor(0.0),GColor(0.0),BColor(0.0),AColor(1.0);
      osg::Vec4  Vcolor;
      WXStream>>RColor>>GColor>>BColor;
      table.push_back(qRgb(RColor*255,GColor*255,BColor*255));  
     }

     return table;
    }

    第三、生成纹理图像,根据颜色配置文件对图像像素 一 一进行赋值

    void COSG3DSurfaceNode::InterpolateAndDrawImage(const std::vector<float>& vecData,QImage* pImage,int xCount,int yCount,int imageSizeX,int imageSizeY)
    {
     assert(xCount > 0);
     assert(yCount > 0);
     assert(vecData.size() == xCount * yCount);

     float min = vecData[0]; 
     float max = vecData[0];
     for(int i = 0; i < vecData.size() ; i++)
     {

      if(max < vecData[i]) max = vecData[i];
      if(min > vecData[i]) min = vecData[i];
     }

     double colorFactor = getColorTable().size() / (max - min);
     /// todo 图片插值
     for(int i = 0; i < imageSizeX; i++)
     {
      for(int j = 0; j < imageSizeY; j++)
      {
       pImage->setPixel(i,j,(vecData[j*xCount + i] - min) * colorFactor );   
      }
     }
     pImage->save(::GetImagePath() + "123.png");
    }


     m_pRootSwitch->addChild(geode);

     

  • 相关阅读:
    11111 Generalized Matrioshkas
    Uva 442 Matrix Chain Multiplication
    Uva 10815 Andy's First Dictionary
    Uva 537 Artificial Intelligence?
    Uva 340 MasterMind Hints
    SCAU 9508 诸葛给我牌(水泥题)
    Uva 10420 List of Conquests(排序水题)
    Uva 409 Excuses, Excuses!
    10/26
    11/2
  • 原文地址:https://www.cnblogs.com/blogofwu/p/5108656.html
Copyright © 2011-2022 走看看