使用OSG建立一個簡單的地形

1.解決方案

在網上參考了一些資料,使用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++

1) 使用TIF格式的DEM

由於不太清楚別的網上資料裏面地形文件是jpg格式的,要知道jpg格式只能8位且沒有地理信息,因此在這裏我直接使用的是GTiff格式的DEM。很奇怪我這裏用osgDB讀取TIF文件失敗了,因此直接採用了GDAL讀取。dom

2) 描述HeightField

使用GDAL打開高程文件(DEM),可以獲取地形的起點位置和間距,將其填充到HeightField中,這樣OSG就肯定了高程點的XY位置。在使用GDAL讀取高程文件(DEM)存儲的高程值到內存中以後,依次填充到HeightField,就肯定了地形的Z位置。最後繪製到節點,地形圖也就繪製出來了。函數

2.存在問題

能夠看到我這裏採用的紋理文件是一個處理好的,範圍剛恰好可以覆蓋的jpg文件。其紋理是自動貼到四個角點的。其實我最初的設想是採用一個DOM(正射影像圖)來實現,經過其地理位置肯定紋理座標,最終無視範圍大小,實現一個DEM(高程)與DOM(影像)的自動疊加。
問題就在於HeightField的點是內部繪製的,我給其賦予的紋理座標老是不正確。我初步嘗試發現一個網格點須要2個紋理座標才能把整個紋理填滿。在這裏但願你們批評指正下,究竟如何給HeightField的點設置紋理位置。spa

3.參考文檔

  1. osg三維重建的兩種方法剖析:三角面片(osgUtil::DelaunayTriangulator)和四角面片(osg::HeightField)
  2. OSG從高程圖建立地形-可運行
  3. OSG從高程圖建立地形
相關文章
相關標籤/搜索