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