DEM轉換為gltf


1. 概述

DEM(地形文件)天然自帶三維信息,可以將其轉換成gltf模型文件。DEM是柵格數據,可以通過GDAL進行讀取;gltf是一種JSON格式,可以采用nlohmann/json進行讀寫。

2. 詳細

直接把代碼貼出來:

#include <iostream>
#include <fstream>
#include <iomanip>
#include <nlohmann\json.hpp>
#include "fifo_map.hpp"

#include <gdal/gdal_priv.h>

using namespace std;
using namespace nlohmann;

// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;

int main()
{
	GDALAllRegister();
	CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");  //支持中文路徑

	my_json gltf;

	gltf["asset"] = {
		{"generator", "CL"},
		{"version", "2.0"} 
	};

	gltf["scene"] = 0;
	gltf["scenes"] = {
		{{"nodes", {0} }}
	};

	gltf["nodes"] = {
		{{"mesh", 0}}
	};

	my_json positionJson;
	positionJson["POSITION"] = 1;
	positionJson["TEXCOORD_0"] = 2;
	
	my_json primitivesJson;
	primitivesJson = {
		{{"attributes", positionJson}, {"indices", 0}, {"material", 0} }
	};	

	gltf["meshes"] = {
		{{"primitives", primitivesJson}}
	};

	my_json pbrJson;
	pbrJson["baseColorTexture"]["index"] = 0;

	gltf["materials"] = {
		{{"pbrMetallicRoughness", pbrJson}}
	};
	
	size_t pointNum = 0;
	size_t binBufNum = 0;
	size_t indicesNum = 0;
	
	{	
		string binPath = "D:/Work/WebGLTutorial/Data/new.bin";
		ofstream binFile(binPath, std::ios::binary);
	
		const char *filePath = "D:/Work/WebGLTutorial/Data/DEM.tif";
		GDALDataset* img = (GDALDataset *)GDALOpen(filePath, GA_ReadOnly);
		if (!img)
		{
			printf("Can't Open Image!");
			return 0;
		}
		int bufWidth = img->GetRasterXSize();   //圖像寬度
		int bufHeight = img->GetRasterYSize();  //圖像高度
		int bandNum = img->GetRasterCount();    //波段數
		if (bandNum != 1)
		{
			printf("DEM波段數不為1");
			return 0;
		}
		int depth = GDALGetDataTypeSize(img->GetRasterBand(1)->GetRasterDataType()) / 8;    //圖像深度
		
		 //獲取地理坐標信息
		double padfTransform[6];
		if (img->GetGeoTransform(padfTransform) == CE_Failure)
		{
			printf("獲取仿射變換參數失敗");
			return 0;
		}

		double startX = padfTransform[0];
		double dX = padfTransform[1];
		double startY = padfTransform[3];
		double dY = padfTransform[5];

		//申請buf
		size_t imgBufNum = (size_t)bufWidth * bufHeight * bandNum;
		float *imgBuf = new float[imgBufNum];

		//讀取
		img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
			GDT_Float32, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);

		pointNum = (size_t)bufWidth * bufHeight;
		size_t position_texture_num = pointNum * 5;
		float *position_texture = new float[position_texture_num];
		
		for (int yi = 0; yi < bufHeight; yi++)
		{
			for (int xi = 0; xi < bufWidth; xi++)
			{
				size_t n = (size_t)(bufWidth * 5) * yi + 5 * xi;
				position_texture[n] = dX * xi;
				position_texture[n+1] = dY * yi;
				size_t m = (size_t)(bufWidth * bandNum) * yi + bandNum * xi;
				position_texture[n + 2] = imgBuf[m];
				position_texture[n + 3] = float(xi) / (bufWidth-1);
				position_texture[n + 4] = float(yi) / (bufHeight-1);			
			}
		}

		//釋放
		delete[] imgBuf;
		imgBuf = nullptr;					
	
		binFile.write((char*)position_texture, position_texture_num * sizeof(float));
	
		size_t vertexBufNum = position_texture_num * sizeof(float);
		binBufNum = binBufNum + vertexBufNum;

		int mod = vertexBufNum % sizeof(uint16_t);	
		if (mod != 0)
		{					
			int spaceNum = sizeof(float) - mod;		
			char *space = new char[spaceNum];
			binBufNum = binBufNum + sizeof(char) * spaceNum;
			memset(space, 0, sizeof(char) * spaceNum);
			binFile.write(space, sizeof(char) * spaceNum);
			delete[] space;
			space = nullptr;
		}
					
		indicesNum = (size_t)(bufWidth - 1) * (bufHeight - 1) * 2 * 3;
		uint16_t *indices = new uint16_t[indicesNum];

		for (int yi = 0; yi < bufHeight-1; yi++)
		{
			for (int xi = 0; xi < bufWidth-1; xi++)
			{
				uint16_t m00 = (uint16_t)(bufWidth * yi + xi) ;
				uint16_t m01 = (uint16_t)(bufWidth * (yi+1) + xi);
				uint16_t m11 = (uint16_t)(bufWidth * (yi + 1) + xi + 1);
				uint16_t m10 = (uint16_t)(bufWidth * yi + xi + 1);

				size_t n = (size_t)(bufWidth - 1) * yi + xi;
				indices[n * 6] = m00;
				indices[n * 6 + 1] = m01;
				indices[n * 6 + 2] = m11;
				indices[n * 6 + 3] = m11;
				indices[n * 6 + 4] = m10;
				indices[n * 6 + 5] = m00;
			}
		}
		
		binFile.write((char*)indices, sizeof(uint16_t) * indicesNum);
		binBufNum = binBufNum + sizeof(uint16_t) * indicesNum;

		delete[] position_texture;
		position_texture = nullptr;

		delete[] indices;
		indices = nullptr;
	}
	   
	gltf["textures"] = {
		{{"sampler", 0}, {"source", 0}}
	};

	gltf["images"] = {
		{{"uri", "tex.jpg"}}
	};

	gltf["samplers"] = {
		{{"magFilter", 9729}, {"minFilter", 9987}, {"wrapS", 33648}, {"wrapT", 33648}}
	};

	   	  
	gltf["buffers"] = {
	{{"uri", "new.bin"}, {"byteLength", binBufNum}}
	};
	
	my_json indicesBufferJson;
	indicesBufferJson["buffer"] = 0;
	indicesBufferJson["byteOffset"] = pointNum * 5 * 4;
	indicesBufferJson["byteLength"] = indicesNum * 2;
	indicesBufferJson["target"] = 34963;

	my_json positionBufferJson;
	positionBufferJson["buffer"] = 0;
	positionBufferJson["byteStride"] = sizeof(float) * 5;
	positionBufferJson["byteOffset"] = 0;
	positionBufferJson["byteLength"] = pointNum * 5 * 4;
	positionBufferJson["target"] = 34962;
	
	gltf["bufferViews"] = {
		indicesBufferJson, positionBufferJson
	};

	my_json indicesAccessors;
	indicesAccessors["bufferView"] = 0;
	indicesAccessors["byteOffset"] = 0;
	indicesAccessors["componentType"] = 5123;
	indicesAccessors["count"] = indicesNum;
	indicesAccessors["type"] = "SCALAR";
	indicesAccessors["max"] = { 18719 };
	indicesAccessors["min"] = { 0 };
	
	my_json positionAccessors;
	positionAccessors["bufferView"] = 1;
	positionAccessors["byteOffset"] = 0;
	positionAccessors["componentType"] = 5126;
	positionAccessors["count"] = pointNum;
	positionAccessors["type"] = "VEC3";
	positionAccessors["max"] = { 770, 0.0,  1261.151611328125 };
	positionAccessors["min"] = { 0.0, -2390,  733.5555419921875 };

	my_json textureAccessors;
	textureAccessors["bufferView"] = 1;
	textureAccessors["byteOffset"] = sizeof(float) * 3;
	textureAccessors["componentType"] = 5126;
	textureAccessors["count"] = pointNum;
	textureAccessors["type"] = "VEC2";
	textureAccessors["max"] = { 1, 1 };
	textureAccessors["min"] = { 0, 0 };

	gltf["accessors"] = {
		indicesAccessors, positionAccessors, textureAccessors
	};	   	  

	string jsonFile = "D:/Work/WebGLTutorial/Data/new.gltf";
	std::ofstream outFile(jsonFile);
	outFile << std::setw(4) << gltf << std::endl;        
}

1.這里使用的DEM是tif格式的圖像,使用GDAL讀取。由於顯示模型文件不需要大坐標,所以沒有把DEM的起始XY坐標值算進去。同時附帶了一張紋理貼圖,正好覆蓋整個DEM的范圍。

2.轉換的的原理非常簡單,就是將DEM的每個網格繪制成兩個三角形,通過頂點索引進行繪制。gltf具體的規范可以參看github上的教程,網上還有相關的中文翻譯

3.原生的nlohmann/json組件寫出來的JSON格式是根據字符串順序排序不是根據插入順序排序的,查閱的時候不方便。所以這里使用了nlohmann::fifo_map容器專門化對象類型。

3. 結果

轉換出來的結果用OSG顯示如下:

地形gltf

4. 參考

[1] github上的gltf教程
[2] gltf教程中文翻譯
[3] nlohmann/json關於保留插入順序的討論


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM