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

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

最终效果图:



时间: 2024-10-18 10:53:46

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

一、查看Linux内核版本命令(两种方法):

一.查看Linux内核版本命令(两种方法): 1.cat /proc/version [[email protected]CentOS home]# cat /proc/versionLinux version 2.6.32-431.el6.x86_64 ([email protected]) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) ) #1 SMP Fri Nov 22 03:15:09 UTC 2013 2.uname -a [

利用颜色和形态学两种方法进行车牌区域提取的OpenCV代码

要想提取车牌号,首先你要定位车牌区域嘛,本文分别两种方法用,即颜色和形态学的方法,对车牌区域进行判定.说得是两种方法,其实两种方法并无多大的区别,只是有一步的判断标准不一样而已,你看了下面整理出的的思路就知道两者的区别真的很小了. 方法一:利用颜色提取车牌区域的思路: ①求得原图像的sobel边缘sobelMat ②在HSV空间内利用车牌颜色阈值对图像进行二值化处理,得到图像bw_blue→ ③由下面的判别标准得到图像bw_blue_edge for (int k = 1; k != heigh

ios图片拉伸两种方法

ios图片拉伸两种方法 UIImage *image = [UIImage imageNamed:@"qq"]; 第一种: // 左端盖宽度 NSInteger leftCapWidth = image.size.width * 0.5f; // 顶端盖高度 NSInteger topCapHeight = image.size.height * 0.5f; // 重新赋值 image = [image stretchableImageWithLeftCapWidth:leftCapW

Android第五期 - 更新自己的apk本地与网络两种方法

首先是本地: ParseXmlService部分: package com.szy.update; import java.io.InputStream; import java.util.HashMap; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element

Linux中生成密钥的两种方法

Linux中生成密钥的两种方法 SSH服务支持一种安全认证机制,即密钥认证.所谓的密钥认证,实际上是使用一对加密字符串,一个称为公钥(publickey), 任何人都可以看到其内容,用于加密:另一个称为密钥(privatekey),只有拥有者才能看到,用于解密.通过公钥加密过的密文使用密钥可以轻松解密,但根据公钥来猜测密钥却十分困难. ssh的密钥认证就是使用了这一特性.服务器和客户端都各自拥有自己的公钥和密钥.如何使用密钥认证登录linux服务器呢? 在使用密钥认证远程登入linux之前,我们

pdf文件怎么修改 修改PDF文件的两种方法

都说PDF格式的文件不能修改,我就呵呵了!不管你们信不信,反正我是不信.因为我会修改PDF文件,并且方法还不止一种.想知道我是怎么修改PDF文件的吗?下面我就告诉大家修改PDF文件的两种方法. 方法一 1.方法一就是将PDF文件转换成一种可容易编辑的文档,如:word.excel.ppt等格式,然后再进行编辑,编辑好后再将其转换成PDF格式,是不是很简单!具体是该如何转换的,下面有详细教程. 2.下载一个PDF转换器,并将它安装在电脑上.工具最好是支持双向转换的那种,如:http://www.x

zabbix使用自己编写脚本模板和zabbix自带模板两种方法添加对指定进程和端口的监控

zabbix使用自己编写脚本模板和zabbix自带模板两种方法添加对指定进程和端口的监控 1.自带监控模板进行os的监控 进入/usr/local/zabbix/etc/zabbix_agentd.conf 配置文件修改 LogRemoteCommands=1     ###开启脚本功能 Server=192.168.5.129     ##修改zabbix指向的服务器: 重启zabbix_agentd.zabbix_server服务 在配置-->主机-->添加主机--> 配置主机信息主

快速生成较大文本文档的两种方法

在学习用FTP发送文件的过程中,需要用到一个比较大的文件进行传输测试.因此百度了一下如何生成指定大小文件的方法,发现在WINDOWS下有两种方法比较实用,记录如下: 第一种方法: 在运行窗口中输入CMD命令回车,进入命令行模式. 在此界面下输入:"fsutil file creatnew test.txt 1024"即可产生一个占用空间为1024字节,名为test.txt的文本. 命令中1024即为该文件占用空间大小,可以任意指定.比如输入1048576就可以产生一个1M大小的文件.当

实现icon和文字垂直居中的两种方法-(vertical-align and line-height)

方法一:vertical-align 在w3school定义:该属性定义行内元素的基线相对于该元素所在行的基线的垂直对齐 百思不得骑姐 然后Google,反正在w3schools上面并没有找到定义 仅仅能写代码測试 而后个人理解,才作出如此解释> 该属性作用的对象:行内元素(inline,inline-block也有行内属性) 其它table-cell 经常使用属性值:top middle bottom 个人理解 个人觉得,普通情况下,这些字母自发"坐落"的这条线就是基线.然后两