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);

     

  • 相关阅读:
    BBB-media配置
    BBB-添加文章及文章中图片
    inclusion_tag模块
    BBB-登录注册
    Django-auth模块
    Django-csrf中间件
    基于django中settings中间件源码思想,实现功能的插拔式设计
    Django之中间件
    Django之cookie与session
    java中public与private还有protect的区别
  • 原文地址:https://www.cnblogs.com/blogofwu/p/5108656.html
Copyright © 2011-2022 走看看