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的範圍。spa

3.必須輸出法向量,並用該法向量數組給對應的geometry賦值orm

具體代碼以下:對象

//建立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);
圖片

//準備紋理圖像get

//生成一副QImage,給其每一個像素用事先定義好的顏色賦值。最後保存成png圖像,保存方式就是image.save(strPath)參數是保存全文件名,包括路徑和後綴。input

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的方式。

2、四邊形面片

這種方式構建的特色以下:

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

第3、生成紋理圖像,根據顏色配置文件對圖像像素 一 一進行賦值

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

相關文章
相關標籤/搜索