1 OSBG
OSGB(OpenSceneGraph Binary)是基于 OpenSceneGraph(OSG) 三維渲染引擎的二進制三維場景數據格式,廣泛用于存儲和傳輸傾斜攝影測量、BIM、點云等大規模三維模型,尤其在國產地理信息與智慧城市項目中應用普遍。
這個格式是國產的,涉及到的軟件就有點尷尬,國產軟件的德行都知道,難用還很多坑。。。
幾個免費的可以下載練手OSBG的地方。
來源 | 內容類型 | 下載方式 | 適用練習 |
---|---|---|---|
GitCode 武漢景點傾斜攝影模型 | 城市 / 景區實景三維 | 項目頁?下載 | 加載、瀏覽、簡單分析 |
GitCode 通用傾斜攝影數據 | 城市 / 建筑模型 | 項目頁?下載(密碼:123321) | 格式測試、性能驗證 |
GitCode 傾斜攝影示例數據 | 小范圍示例場景 | 項目頁?下載 | 教學演示、功能測試 |
GitCode 三維山地模型(含 3DTiles) | 山地河谷、村落、BIM | 項目頁?下載 | 多格式兼容、場景分析 |
GitCode 城市級傾斜攝影數據 | 大規模城市模型 | 項目頁?下載 | 性能測試、城市級渲染 |
百度網盤 臺北市傾斜模型 | 城市級傾斜模型 | 網盤(提取碼:ae9w) | 城市建模、地形分析 |
2 格式轉換
目前基本上就幾個軟件能弄,一個是CesiumLab,一個是SuperMap iDesktop。剩下還有很多不知名的軟件。。。
2.1?CesiumLab
CesiumLab聽著像官方產品,實際不是,是北京一個公司。而且還不能單獨裝,先要裝一個他們的全家桶“地球可視化實驗室”,然后在里面安裝這個CesiumLab。
安裝了之后點運行,15分鐘都沒能啟動起來。看了一下changelog,居然還有用戶名是中文就異常的BUG。。。
好不容易啟動了找到怎么轉換,研究了半天參數準備轉換。結果彈出來。。。讓去新地方注冊。
看了一下。
真實姓名,電話號碼,工作單位全部都要填進去,就差沒弄個戶口本進去。折騰完了終于看可以轉換了,一點開始轉換。直接沒了。。。
又看了一下。
貌似還得上傳工牌,公司蓋章。。。
算了,放棄治療了。。
2.2 SuperMap iDesktop
下載之后又是要授權。。
我看我這邊全是否,估計著是要錢才行。算了吧。。。
2.3 老子云
這個是B站看到的,名字確實有點點霸氣。網址是:https://www.laozicloud.com/
也是要注冊一大堆,估計以后也是要收費的,不過確實還能免費用。
轉換的時間很長,110MB的數據就差不多半個小時。
3 3Dtiles
轉換出來的數據大概是這樣的。
每個tile里面是這樣。一個json和一堆glb
3.1?rootTileset.json
{"asset": {"gltfUpAxis": "Z","version": "1.0"},"extensionsUsed": ["3DTILES_content_gltf"],"geometricError": 2000,"root": {"boundingVolume": {"box": [-74.0523910522461,22.896909713745117,309.3908233642578,85.72734832763672,0.0,0.0,0.0,112.13745307922363,0.0,0.0,0.0,56.62489318847656]},"children": [{"boundingVolume": {"box": [-109.0,-44.450782775878906,296.45343017578125,43.68748474121094,0,0,0,43.6874885559082,0,0,0,43.6875]},"content": {"uri": "./Tile_+001_+000/tileset.json"},"geometricError": 4.36875},{"boundingVolume": {"box": [-95.49225616455078,16.0,304.4522705078125,42.26628875732422,0,0,0,42.266265869140625,0,0,0,42.266265869140625]},"content": {"uri": "./Tile_+001_+001/tileset.json"},"geometricError": 4.226628875732422},{"boundingVolume": {"box": [-109.0,84.25462341308594,310.2703857421875,50.77973937988281,0,0,0,50.77973937988281,0,0,0,50.77972412109375]},"content": {"uri": "./Tile_+001_+002/tileset.json"},"geometricError": 5.077973937988282},{"boundingVolume": {"box": [-39.0,-41.21886444091797,299.5376281738281,44.396854400634766,0,0,0,48.02167892456055,0,0,0,44.3968505859375]},"content": {"uri": "./Tile_+002_+000/tileset.json"},"geometricError": 4.802167892456055},{"boundingVolume": {"box": [-39.81702423095703,16.0,314.52374267578125,51.491981506347656,0,0,0,51.491981506347656,0,0,0,51.491973876953125]},"content": {"uri": "./Tile_+002_+001/tileset.json"},"geometricError": 5.149198150634766},{"boundingVolume": {"box": [-39.81702423095703,69.45303344726563,304.82012939453125,39.80249786376953,0,0,0,39.80249786376953,0,0,0,40.469970703125]},"content": {"uri": "./Tile_+002_+002/tileset.json"},"geometricError": 4.0469970703125}],"geometricError": 11.213745307922364,"transform": [-0.0,1.0,0.0,0.0,0.0,-0.0,1.0,0.0,1.0,0.0,0.0,0.0,6378137.0,0.0,0.0,1.0]}
}
asset
數據集的基本信息,版本、坐標軸方向等。geometricError
頂層誤差閾值,控制LOD(細節層級)切換。root
根瓦片(root tile),定義了整個模型的范圍和第一個瓦片。
boundingVolume:瓦片的空間范圍(box / region / sphere)。
geometricError:當前瓦片的最大幾何誤差,決定是否繼續向下加載子瓦片。
refine:
REPLACE
或ADD
,決定加載子瓦片時是替換父瓦片,還是疊加細節。children:子瓦片信息。
content
指向實際的模型文件(如.b3dm
、.glb
、.pnts
)。
3.2?tileset.json
格式和root幾乎大同小異。
實在太長,就簡單摘抄一部分吧。
{"asset": {"extensionsUsed": ["3DTILES_content_gltf"],"gltfUpAxis": "Z","version": "1.0"},"geometricError": 1000,"root": {"boundingVolume": {"box": [-109.0,-44.450782775878906,296.45343017578125,43.68748474121094,0,0,0,43.6874885559082,0,0,0,43.6875]},"children": [{"boundingVolume": {"box": [-109.0,-44.529869079589844,296.2623291015625,43.747650146484375,0,0,0,43.747650146484375,0,0,0,43.747650146484375]},"children": [{"boundingVolume": {"box": [-109.0,-44.41993713378906,296.2623291015625,43.68302917480469,0,0,0,43.68303298950195,0,0,0,43.68304443359375]},"children": [{"boundingVolume": {"box": [-108.79536437988281,-31.627532958984375,297.58258056640625,39.55524444580078,0,0,0,39.55524444580078,0,0,0,39.55523681640625]},"children": [{"boundingVolume": {"box": [-117.75,-31.122161865234375,296.3058776855469,17.014083862304688,0,0,0,17.01408576965332,0,0,0,17.01409912109375]},"children": [{"boundingVolume": {"box": [-117.72776794433594,-37.15178680419922,296.82281494140625,10.89080810546875,0,0,0,10.868576049804688,0,0,0,10.868560791015625]},"children": [{"boundingVolume": {"box": [-117.72532653808594,-37.15178680419922,296.8204345703125,10.850723266601563,0,0,0,10.826057434082031,0,0,0,10.8260498046875]},"content": {"boundingVolume": {"box": [-117.72532653808594,-37.15178680419922,296.8204345703125,10.850723266601563,0,0,0,10.826057434082031,0,0,0,10.8260498046875]},"uri": "./Tile_+001_+000_L22_001200.glb"},"geometricError": 1.0850723266601563,"name": "Tile_+001_+000_L22_001200.osgb"}],
可以看到,root里面的uri是下層的json。這里則是對應的glb文件。
3.3?Tile_+001_+000_L17_0.glb
沒啥好多說的,就是切小了的glb文件。關于glb在前面寫過了。。。
3.4 小節
從上面可以看出。Cesium 的 3D Tiles(比如 3DTileset)本質上就是:
把一個很大的 3D 模型(GLB/OBJ/las 點云等)切分成多個小塊,再通過一個 JSON 索引文件(tileset.json) 來描述這些小塊的層級、邊界和加載邏輯。這樣 Cesium 在瀏覽時就可以做到 按需加載、分層細節(LOD)管理,避免一次性加載超大模型。
4 Cesium加載
最后還是能加載了。只是問題很多,主要是方向。
代碼如下:
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Cesium基礎地球</title><script src="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Cesium.js"></script><link href="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Widgets/widgets.css" rel="stylesheet"><style>#cesiumContainer { width: 100%; height: 100vh; }</style>
</head>
<body><div id="cesiumContainer"></div>
<script>// 初始化 Cesium ViewerCesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzYjVkMmRhYy03OGMxLTQwM2EtYWY0Ny00MDM4YjhjZmVkNzIiLCJpZCI6MzA0Mzc4LCJpYXQiOjE3NDc3MzE5NDN9.kzP84v1ibzx6iJP_ESqc-PiJ6-fTbHQvCR2KMc9lvws';const viewer = new Cesium.Viewer("cesiumContainer", {terrainProvider: new Cesium.EllipsoidTerrainProvider()});// 加載 3D Tiles (指向 rootTileset.json)const tileset = new Cesium.Cesium3DTileset({url: "data/rootTileset.json" // 相對路徑,index.html 同級目錄下的 data 文件夾});viewer.scene.primitives.add(tileset);tileset.readyPromise.then(function() {const longitude = 139.7101;const latitude = 35.6852;const height = 50.0;const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);// 設置旋轉角度(弧度制)const heading = Cesium.Math.toRadians(90); // 水平方向const pitch = Cesium.Math.toRadians(0); // 上下傾斜const roll = Cesium.Math.toRadians(300); // 翻滾const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);const modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(position, hpr);// 如果需要縮放,可以加這一句(比如縮小 0.1 倍)const scale = 0.1;tileset.modelMatrix = Cesium.Matrix4.multiplyByUniformScale(modelMatrix, scale, new Cesium.Matrix4());// 相機飛過去viewer.zoomTo(tileset);
});</script></body>
</html>