博客 / 詳情

返回

JSAPIThree 加載 3D Tiles 學習筆記:大規模三維場景渲染

在實際項目中,我們經常需要加載大規模的三維場景數據,比如城市建築模型、地形數據等。3D Tiles 是 Cesium 提出的開放標準,用於高效地流式傳輸和渲染大量 3D 內容。今天就來學習一下如何在 mapvthree 中使用 3D Tiles。

瞭解 3D Tiles

3D Tiles 是一種用於流式傳輸和渲染大量 3D 內容的開放標準,具有以下特點:

  • 層次化結構:使用空間層次結構組織數據,支持細節層次(LOD)
  • 流式傳輸:按需加載,只加載視野內的數據
  • 高性能:通過剔除、LOD 等技術優化渲染性能
  • 標準化:開放標準,支持多種數據格式

我的理解:3D Tiles 就像是一個智能的三維場景管理系統,能夠根據相機位置和視角,自動決定加載哪些數據,以及加載到哪個細節層次。

第一步:從 URL 加載 3D Tiles

最簡單的方式是從 URL 加載 3D Tiles 數據。

基本使用

import * as mapvthree from '@baidumap/mapv-three';

const container = document.getElementById('container');

const engine = new mapvthree.Engine(container, {
    map: {
        center: [105.931, 29.349, 280],
        range: 2000,
        pitch: 75,
        provider: null,
    },
});

// 從 URL 加載 3D Tiles
const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
}));

我的發現:只需要提供 tileset.json 的 URL,引擎會自動加載整個 3D Tiles 數據集。

我的理解

  • tileset.json 是 3D Tiles 的入口文件,定義了整個數據集的層次結構
  • 引擎會根據相機位置自動加載需要的瓦片
  • 支持多種 3D Tiles 格式(b3dm、i3dm、pnts 等)

第二步:從 Cesium Ion 加載

如果數據存儲在 Cesium Ion 上,可以使用 asset ID 加載。

使用 fromAssetId

// 從 Cesium Ion asset ID 加載
const tileset = await mapvthree.Default3DTiles.fromAssetId('assetId', {
    errorTarget: 16,
});

engine.add(tileset);

我的發現fromAssetId 是靜態方法,返回一個 Promise,需要等待加載完成。

我的理解

  • 需要先配置 Cesium Ion AccessToken
  • asset ID 是 Cesium Ion 中資源的唯一標識
  • 可以使用配置參數來優化加載和渲染

第三步:性能優化配置

3D Tiles 提供了很多性能優化參數,合理配置可以顯著提升渲染性能。

errorTarget:屏幕空間誤差

errorTarget 控制屏幕空間誤差目標值,影響 LOD 的切換時機。

const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    errorTarget: 16, // 默認值,數值越小,細節越多,性能開銷越大
}));

我的理解

  • 數值越小,顯示的細節越多,但性能開銷越大
  • 數值越大,顯示的細節越少,但性能更好
  • 需要根據場景和性能要求調整

cullRequestsWhileMoving:移動時剔除

cullRequestsWhileMoving 控制相機移動時是否剔除請求,可以提升移動時的性能。

const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    cullRequestsWhileMoving: true, // 移動時剔除請求,提升性能
}));

我的發現:開啓後,相機快速移動時不會加載新瓦片,等移動停止後再加載,可以避免不必要的網絡請求。

cullWithChildrenBounds:使用子節點邊界剔除

cullWithChildrenBounds 控制是否使用子節點邊界進行剔除,可以更精確地判斷是否需要加載子節點。

const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    cullWithChildrenBounds: true, // 使用子節點邊界進行剔除
}));

我的理解:開啓後,會使用子節點的邊界框來判斷是否需要加載,可以更精確地剔除不可見的節點。

loadSiblings:加載兄弟節點

loadSiblings 控制是否加載兄弟節點,可以預加載相鄰的瓦片。

const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    loadSiblings: true, // 加載兄弟節點,預加載相鄰瓦片
}));

我的發現:開啓後,會預加載相鄰的瓦片,當相機移動到相鄰區域時,數據已經準備好了,可以提升用户體驗。

其他性能參數

還有一些高級性能參數:

const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    // 緩存配置
    cacheBytes: 512 * 1024 * 1024, // 緩存大小(字節)
    
    // 動態屏幕空間誤差
    dynamicScreenSpaceError: true,
    dynamicScreenSpaceErrorDensity: 0.00278,
    dynamicScreenSpaceErrorHeightFalloff: 0.25,
    
    // 注視點渲染(Foveated Rendering)
    foveatedScreenSpaceError: true,
    foveatedConeSize: 0.1,
    foveatedMinimumScreenSpaceErrorRelaxation: 2.0,
    
    // 其他配置
    forceUnlit: false, // 強制無光照模式
    progressiveResolutionHeightFraction: 0.3,
}));

我的理解:這些參數主要用於高級優化,一般使用默認值即可,除非有特殊的性能需求。

第四步:運行時調整參數

可以在運行時動態調整參數,實時優化性能。

const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    errorTarget: 16,
}));

// 運行時調整參數
tileset.errorTarget = 8; // 提高細節
tileset.cullRequestsWhileMoving = true; // 開啓移動剔除
tileset.loadSiblings = true; // 開啓兄弟節點加載

我的發現:可以根據場景需求動態調整參數,比如在性能不足時降低 errorTarget,在需要流暢移動時開啓 cullRequestsWhileMoving

第五步:完整示例

我想寫一個完整的示例,把學到的都用上:

import * as mapvthree from '@baidumap/mapv-three';

const container = document.getElementById('container');

const engine = new mapvthree.Engine(container, {
    map: {
        center: [105.931, 29.349, 280],
        range: 2000,
        pitch: 75,
        provider: null,
    },
});

// 從 URL 加載 3D Tiles,配置性能參數
const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    errorTarget: 16,
    cullRequestsWhileMoving: true,
    cullWithChildrenBounds: true,
    loadSiblings: true,
}));

我的感受:掌握了這些配置,就可以根據實際需求優化 3D Tiles 的加載和渲染性能了!

第六步:踩過的坑

作為一個初學者,我踩了不少坑,記錄下來避免再犯:

坑 1:3D Tiles 不顯示

原因:URL 路徑錯誤,或者 tileset.json 文件不存在。

解決

  1. 檢查 URL 路徑是否正確
  2. 確認 tileset.json 文件存在且可訪問
  3. 檢查瀏覽器控制枱是否有錯誤信息

坑 2:加載很慢

原因:數據量大,或者網絡慢,或者性能參數配置不當。

解決

  1. 檢查網絡連接
  2. 調整 errorTarget 參數,降低細節要求
  3. 開啓 cullRequestsWhileMoving 優化移動性能
  4. 檢查數據是否過大,考慮使用 LOD 優化

坑 3:內存佔用過高

原因:緩存設置過大,或者加載了太多瓦片。

解決

  1. 調整 cacheBytes 參數,限制緩存大小
  2. 降低 errorTarget,減少加載的瓦片數量
  3. 開啓 cullWithChildrenBounds 精確剔除

坑 4:相機移動卡頓

原因:移動時加載了太多新瓦片,或者性能參數配置不當。

解決

  1. 開啓 cullRequestsWhileMoving,移動時暫停加載
  2. 開啓 loadSiblings,預加載相鄰瓦片
  3. 調整 errorTarget,降低細節要求

坑 5:Cesium Ion 加載失敗

原因:沒有配置 Cesium Ion AccessToken,或者 asset ID 錯誤。

解決

  1. 確保配置了 Cesium Ion AccessToken
  2. 檢查 asset ID 是否正確
  3. 確認 asset ID 對應的資源存在且有訪問權限

我的學習總結

經過這一天的學習,我掌握了:

  1. 從 URL 加載:使用 new Default3DTiles({ url }) 從 URL 加載
  2. 從 Cesium Ion 加載:使用 Default3DTiles.fromAssetId() 從 Cesium Ion 加載
  3. 性能優化:理解 errorTargetcullRequestsWhileMoving 等參數的作用
  4. 運行時調整:可以在運行時動態調整參數

我的感受:3D Tiles 功能很強大,但配置參數也比較多。關鍵是要理解每個參數的作用,然後根據實際需求進行優化。性能優化是一個平衡的過程,需要在細節和性能之間找到平衡點!

下一步計劃

  1. 學習更多 3D Tiles 的高級功能
  2. 嘗試創建自定義的 3D Tiles 數據
  3. 做一個完整的大規模場景展示項目

學習筆記就到這裏啦!作為一個初學者,我覺得 3D Tiles 功能很強大,但配置參數也比較多。關鍵是要理解每個參數的作用,然後根據實際需求進行優化。希望我的筆記能幫到其他初學者!大家一起加油!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.