丁文兵 發表於 2025-12-16 16:12:00

JSAPIThree 加载 3D Tiles 学习笔记:大规模三维场景渲染

<blockquote>
<p>在实际项目中,我们经常需要加载大规模的三维场景数据,比如城市建筑模型、地形数据等。3D Tiles 是 Cesium 提出的开放标准,用于高效地流式传输和渲染大量 3D 内容。今天就来学习一下如何在 mapvthree 中使用 3D Tiles。</p>
</blockquote>
<h2 id="了解-3d-tiles">了解 3D Tiles</h2>
<p>3D Tiles 是一种用于流式传输和渲染大量 3D 内容的开放标准,具有以下特点:</p>
<ul>
<li><strong>层次化结构</strong>:使用空间层次结构组织数据,支持细节层次(LOD)</li>
<li><strong>流式传输</strong>:按需加载,只加载视野内的数据</li>
<li><strong>高性能</strong>:通过剔除、LOD 等技术优化渲染性能</li>
<li><strong>标准化</strong>:开放标准,支持多种数据格式</li>
</ul>
<p><strong>我的理解</strong>:3D Tiles 就像是一个智能的三维场景管理系统,能够根据相机位置和视角,自动决定加载哪些数据,以及加载到哪个细节层次。</p>
<h2 id="第一步从-url-加载-3d-tiles">第一步:从 URL 加载 3D Tiles</h2>
<p>最简单的方式是从 URL 加载 3D Tiles 数据。</p>
<h3 id="基本使用">基本使用</h3>
<pre><code class="language-js">import * as mapvthree from '@baidumap/mapv-three';

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

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

// 从 URL 加载 3D Tiles
const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
}));
</code></pre>
<p><strong>我的发现</strong>:只需要提供 <code>tileset.json</code> 的 URL,引擎会自动加载整个 3D Tiles 数据集。</p>
<p><strong>我的理解</strong>:</p>
<ul>
<li><code>tileset.json</code> 是 3D Tiles 的入口文件,定义了整个数据集的层次结构</li>
<li>引擎会根据相机位置自动加载需要的瓦片</li>
<li>支持多种 3D Tiles 格式(b3dm、i3dm、pnts 等)</li>
</ul>
<h2 id="第二步从-cesium-ion-加载">第二步:从 Cesium Ion 加载</h2>
<p>如果数据存储在 Cesium Ion 上,可以使用 asset ID 加载。</p>
<h3 id="使用-fromassetid">使用 fromAssetId</h3>
<pre><code class="language-js">// 从 Cesium Ion asset ID 加载
const tileset = await mapvthree.Default3DTiles.fromAssetId('assetId', {
    errorTarget: 16,
});

engine.add(tileset);
</code></pre>
<p><strong>我的发现</strong>:<code>fromAssetId</code> 是静态方法,返回一个 Promise,需要等待加载完成。</p>
<p><strong>我的理解</strong>:</p>
<ul>
<li>需要先配置 Cesium Ion AccessToken</li>
<li>asset ID 是 Cesium Ion 中资源的唯一标识</li>
<li>可以使用配置参数来优化加载和渲染</li>
</ul>
<h2 id="第三步性能优化配置">第三步:性能优化配置</h2>
<p>3D Tiles 提供了很多性能优化参数,合理配置可以显著提升渲染性能。</p>
<h3 id="errortarget屏幕空间误差">errorTarget:屏幕空间误差</h3>
<p><code>errorTarget</code> 控制屏幕空间误差目标值,影响 LOD 的切换时机。</p>
<pre><code class="language-js">const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    errorTarget: 16, // 默认值,数值越小,细节越多,性能开销越大
}));
</code></pre>
<p><strong>我的理解</strong>:</p>
<ul>
<li>数值越小,显示的细节越多,但性能开销越大</li>
<li>数值越大,显示的细节越少,但性能更好</li>
<li>需要根据场景和性能要求调整</li>
</ul>
<h3 id="cullrequestswhilemoving移动时剔除">cullRequestsWhileMoving:移动时剔除</h3>
<p><code>cullRequestsWhileMoving</code> 控制相机移动时是否剔除请求,可以提升移动时的性能。</p>
<pre><code class="language-js">const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    cullRequestsWhileMoving: true, // 移动时剔除请求,提升性能
}));
</code></pre>
<p><strong>我的发现</strong>:开启后,相机快速移动时不会加载新瓦片,等移动停止后再加载,可以避免不必要的网络请求。</p>
<h3 id="cullwithchildrenbounds使用子节点边界剔除">cullWithChildrenBounds:使用子节点边界剔除</h3>
<p><code>cullWithChildrenBounds</code> 控制是否使用子节点边界进行剔除,可以更精确地判断是否需要加载子节点。</p>
<pre><code class="language-js">const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    cullWithChildrenBounds: true, // 使用子节点边界进行剔除
}));
</code></pre>
<p><strong>我的理解</strong>:开启后,会使用子节点的边界框来判断是否需要加载,可以更精确地剔除不可见的节点。</p>
<h3 id="loadsiblings加载兄弟节点">loadSiblings:加载兄弟节点</h3>
<p><code>loadSiblings</code> 控制是否加载兄弟节点,可以预加载相邻的瓦片。</p>
<pre><code class="language-js">const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    loadSiblings: true, // 加载兄弟节点,预加载相邻瓦片
}));
</code></pre>
<p><strong>我的发现</strong>:开启后,会预加载相邻的瓦片,当相机移动到相邻区域时,数据已经准备好了,可以提升用户体验。</p>
<h3 id="其他性能参数">其他性能参数</h3>
<p>还有一些高级性能参数:</p>
<pre><code class="language-js">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,
}));
</code></pre>
<p><strong>我的理解</strong>:这些参数主要用于高级优化,一般使用默认值即可,除非有特殊的性能需求。</p>
<h2 id="第四步运行时调整参数">第四步:运行时调整参数</h2>
<p>可以在运行时动态调整参数,实时优化性能。</p>
<pre><code class="language-js">const tileset = engine.add(new mapvthree.Default3DTiles({
    url: 'data/3dtiles/tileset.json',
    errorTarget: 16,
}));

// 运行时调整参数
tileset.errorTarget = 8; // 提高细节
tileset.cullRequestsWhileMoving = true; // 开启移动剔除
tileset.loadSiblings = true; // 开启兄弟节点加载
</code></pre>
<p><strong>我的发现</strong>:可以根据场景需求动态调整参数,比如在性能不足时降低 <code>errorTarget</code>,在需要流畅移动时开启 <code>cullRequestsWhileMoving</code>。</p>
<h2 id="第五步完整示例">第五步:完整示例</h2>
<p>我想写一个完整的示例,把学到的都用上:</p>
<pre><code class="language-js">import * as mapvthree from '@baidumap/mapv-three';

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

const engine = new mapvthree.Engine(container, {
    map: {
      center: ,
      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,
}));
</code></pre>
<p><strong>我的感受</strong>:掌握了这些配置,就可以根据实际需求优化 3D Tiles 的加载和渲染性能了!</p>
<h2 id="第六步踩过的坑">第六步:踩过的坑</h2>
<p>作为一个初学者,我踩了不少坑,记录下来避免再犯:</p>
<h3 id="坑-13d-tiles-不显示">坑 1:3D Tiles 不显示</h3>
<p><strong>原因</strong>:URL 路径错误,或者 tileset.json 文件不存在。</p>
<p><strong>解决</strong>:</p>
<ol>
<li>检查 URL 路径是否正确</li>
<li>确认 tileset.json 文件存在且可访问</li>
<li>检查浏览器控制台是否有错误信息</li>
</ol>
<h3 id="坑-2加载很慢">坑 2:加载很慢</h3>
<p><strong>原因</strong>:数据量大,或者网络慢,或者性能参数配置不当。</p>
<p><strong>解决</strong>:</p>
<ol>
<li>检查网络连接</li>
<li>调整 <code>errorTarget</code> 参数,降低细节要求</li>
<li>开启 <code>cullRequestsWhileMoving</code> 优化移动性能</li>
<li>检查数据是否过大,考虑使用 LOD 优化</li>
</ol>
<h3 id="坑-3内存占用过高">坑 3:内存占用过高</h3>
<p><strong>原因</strong>:缓存设置过大,或者加载了太多瓦片。</p>
<p><strong>解决</strong>:</p>
<ol>
<li>调整 <code>cacheBytes</code> 参数,限制缓存大小</li>
<li>降低 <code>errorTarget</code>,减少加载的瓦片数量</li>
<li>开启 <code>cullWithChildrenBounds</code> 精确剔除</li>
</ol>
<h3 id="坑-4相机移动卡顿">坑 4:相机移动卡顿</h3>
<p><strong>原因</strong>:移动时加载了太多新瓦片,或者性能参数配置不当。</p>
<p><strong>解决</strong>:</p>
<ol>
<li>开启 <code>cullRequestsWhileMoving</code>,移动时暂停加载</li>
<li>开启 <code>loadSiblings</code>,预加载相邻瓦片</li>
<li>调整 <code>errorTarget</code>,降低细节要求</li>
</ol>
<h3 id="坑-5cesium-ion-加载失败">坑 5:Cesium Ion 加载失败</h3>
<p><strong>原因</strong>:没有配置 Cesium Ion AccessToken,或者 asset ID 错误。</p>
<p><strong>解决</strong>:</p>
<ol>
<li>确保配置了 Cesium Ion AccessToken</li>
<li>检查 asset ID 是否正确</li>
<li>确认 asset ID 对应的资源存在且有访问权限</li>
</ol>
<h2 id="我的学习总结">我的学习总结</h2>
<p>经过这一天的学习,我掌握了:</p>
<ol>
<li><strong>从 URL 加载</strong>:使用 <code>new Default3DTiles({ url })</code> 从 URL 加载</li>
<li><strong>从 Cesium Ion 加载</strong>:使用 <code>Default3DTiles.fromAssetId()</code> 从 Cesium Ion 加载</li>
<li><strong>性能优化</strong>:理解 <code>errorTarget</code>、<code>cullRequestsWhileMoving</code> 等参数的作用</li>
<li><strong>运行时调整</strong>:可以在运行时动态调整参数</li>
</ol>
<p><strong>我的感受</strong>:3D Tiles 功能很强大,但配置参数也比较多。关键是要理解每个参数的作用,然后根据实际需求进行优化。性能优化是一个平衡的过程,需要在细节和性能之间找到平衡点!</p>
<p><strong>下一步计划</strong>:</p>
<ol>
<li>学习更多 3D Tiles 的高级功能</li>
<li>尝试创建自定义的 3D Tiles 数据</li>
<li>做一个完整的大规模场景展示项目</li>
</ol>
<hr>
<blockquote>
<p>学习笔记就到这里啦!作为一个初学者,我觉得 3D Tiles 功能很强大,但配置参数也比较多。关键是要理解每个参数的作用,然后根据实际需求进行优化。希望我的笔记能帮到其他初学者!大家一起加油!</p>
</blockquote><br><br>
来源:https://www.cnblogs.com/map-3d-vis/p/19357873
頁: [1]
查看完整版本: JSAPIThree 加载 3D Tiles 学习笔记:大规模三维场景渲染