在网上参考了一些资料,使用OSG建立地形最简单的办法就是使用OSG::HeightField类,它是描述相似于DEM网格的四角面片。首先给出具体实现代码:ios
#include <iostream> #include <Windows.h> #include <osgViewer/Viewer> #include <osgDB/ReadFile> #include <osg/Texture2D> #include <osg/ShapeDrawable> #include <gdal_priv.h> using namespace std; using namespace osg; using namespace osgViewer; //实现函数:从高程图建立地形 osg::Node* createHeightField(std::string heightFile, std::string texFile) { //读取高度文件 GDALAllRegister(); //GDAL全部操做都须要先注册格式 CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO"); //支持中文路径 GDALDataset* img = (GDALDataset *)GDALOpen(heightFile.c_str(), GA_ReadOnly); if (!img) { return nullptr; } //读取基本参数 int imgWidth = img->GetRasterXSize(); //图像宽度 int imgHeight = img->GetRasterYSize(); //图像高度 int bandNum = img->GetRasterCount(); //波段数 int depth = GDALGetDataTypeSize(img->GetRasterBand(1)->GetRasterDataType()) / 8; //图像深度 //获取地理坐标信息 double padfTransform[6]; if (img->GetGeoTransform(padfTransform) == CE_Failure) { return nullptr; } double startX = padfTransform[0] + 0.5 * padfTransform[1]; //左上角点坐标X double dX = padfTransform[1]; //X方向的分辨率 double startY = padfTransform[3] + padfTransform[5] * imgHeight - 0.5 * padfTransform[5]; //左下角点坐标Y double dY = -padfTransform[5]; //Y方向的分辨率 //申请buf int bufWidth = imgWidth; int bufHeight = imgHeight; size_t imgBufNum = (size_t)bufWidth * bufHeight * bandNum; float *imgBuf = new float[imgBufNum]; //读取 size_t imgBufOffset = (size_t)bufWidth * (bufHeight - 1) * bandNum; img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf + imgBufOffset, bufWidth, bufHeight, GDT_Float32, bandNum, nullptr, bandNum*depth, -bufWidth*bandNum*depth, depth); //定义并设置高度文件 osg::ref_ptr<osg::HeightField> heightField = new osg::HeightField(); heightField->allocate(imgWidth, imgHeight); //申请空间 heightField->setOrigin(osg::Vec3(startX, startY, 0)); //起始位置 heightField->setXInterval(dX); //间距X heightField->setYInterval(dY); //间距Y heightField->setSkirtHeight(1.0f); //填充高度值 for (int r = 0; r < imgHeight; r++) { for (int c = 0; c < imgWidth; c++) { size_t m = (size_t)r * imgWidth + c; heightField->setHeight(c, r, imgBuf[m]); } } //释放 delete[] imgBuf; imgBuf = nullptr; //节点 osg::Geode* geode = new osg::Geode(); osg::ref_ptr<osg::ShapeDrawable> heightShape = new osg::ShapeDrawable(heightField.get()); geode->addDrawable(heightShape); //设置纹理 osg::ref_ptr<osg::Image> texImage = osgDB::readImageFile(texFile); osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D; tex->setImage(texImage); tex->setDataVariance(osg::Object::DYNAMIC); //渲染状态 osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet(); stateset->setTextureAttributeAndModes(0, tex.get(), osg::StateAttribute::ON); geode->setStateSet(stateset.get()); return geode; } int main() { osgViewer::Viewer viewer; osg::ref_ptr<osg::Group> group = new osg::Group; std::string heightFile = "D:\\Data\\dst.tif"; std::string texFile = "D:\\Data\\dom3_Level_19.jpg"; group->addChild(createHeightField(heightFile, texFile)); viewer.setSceneData(group); viewer.setUpViewInWindow(100, 100, 800, 600); return viewer.run(); }
其运行结果以下,显示的是美国大峡谷(Grand Canyon)中的一小块:
c++
由于不太清楚别的网上资料里面地形文件是jpg格式的,要知道jpg格式只能8位且没有地理信息,因此在这里我直接使用的是GTiff格式的DEM。很奇怪我这里用osgDB读取TIF文件失败了,因此直接采用了GDAL读取。dom
使用GDAL打开高程文件(DEM),可以获取地形的起点位置和间距,将其填充到HeightField中,这样OSG就肯定了高程点的XY位置。在使用GDAL读取高程文件(DEM)存储的高程值到内存中以后,依次填充到HeightField,就肯定了地形的Z位置。最后绘制到节点,地形图也就绘制出来了。函数
能够看到我这里采用的纹理文件是一个处理好的,范围刚恰好可以覆盖的jpg文件。其纹理是自动贴到四个角点的。其实我最初的设想是采用一个DOM(正射影像图)来实现,经过其地理位置肯定纹理坐标,最终无视范围大小,实现一个DEM(高程)与DOM(影像)的自动叠加。
问题就在于HeightField的点是内部绘制的,我给其赋予的纹理坐标老是不正确。我初步尝试发现一个网格点须要2个纹理坐标才能把整个纹理填满。在这里但愿你们批评指正下,究竟如何给HeightField的点设置纹理位置。spa