osg三维重建的两种方法剖析:三角面片(osgUtil::DelaunayTriangulator)和四角面片(osg::HeightField)

来源:互联网 发布:webpack压缩js ES6语法 编辑:程序博客网 时间:2024/04/29 21:39

最近项目中需要利用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);


最终效果图:



1 0
原创粉丝点击