Zzc fd3a6c22cc perf(3d-tiles): 优化渲染参数以提升模型细节并更新加载资源
调整 maximumScreenSpaceError 至 2.0,并启用动态调整,以增强 3D 模型精度,同时平衡性能。另外,为保持一致性,重命名加载 GIF 文件。
2025-11-19 17:18:10 +08:00

177 lines
6.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ref } from 'vue'
import * as Cesium from 'cesium'
import { getModelCompareConfig } from '../config/modelCompare.config'
/**
* 3D Tiles加载管理 Composable
* 负责加载灾前/灾后3D模型数据并支持分屏对比功能
*/
export function use3DTiles() {
const beforeTileset = ref(null)
const afterTileset = ref(null)
/**
* 加载3D Tileset
* @param {Cesium.Viewer} viewer - Cesium Viewer 实例
* @param {string} sceneType - 场景类型:'before' 或 'after'
* @param {boolean} autoZoom - 是否自动缩放到模型默认false保持用户设置的相机位置
* @param {Cesium.SplitDirection} splitDirection - 分割方向(默认为 NONE
*/
const load3DTileset = async (viewer, sceneType = 'after', autoZoom = false, splitDirection = Cesium.SplitDirection.NONE) => {
if (!viewer) return null
try {
console.log(`[use3DTiles] 正在加载${sceneType === 'after' ? '灾后' : '灾前'}3D模型...`)
// 从配置中获取 URL
const config = getModelCompareConfig()
const tilesetConfig = sceneType === 'after' ? config.after3DTiles : config.before3DTiles
const url = tilesetConfig.url
const tileset = await Cesium.Cesium3DTileset.fromUrl(url, {
skipLevelOfDetail: true,
baseScreenSpaceError: 1024,
skipScreenSpaceErrorFactor: 16,
skipLevels: 1,
immediatelyLoadDesiredLevelOfDetail: false,
loadSiblings: false,
maximumScreenSpaceError: 2.0, // 降低到2.0以提高模型精细度原来是16.0
dynamicScreenSpaceError: true, // 启用动态屏幕空间误差调整
dynamicScreenSpaceErrorDensity: 0.00278, // 启用密度调整
dynamicScreenSpaceErrorFactor: 4.0, // 动态因子
foveatedScreenSpaceError: true, // 启用视锥细化
foveatedConeSize: 0.1, // 视锥大小
foveatedMinimumScreenSpaceErrorRelaxation: 0.0 // 最小放松
})
// 将tileset添加到viewer的primitives
viewer.scene.primitives.add(tileset)
// 设置splitDirection用于对比模式
tileset.splitDirection = splitDirection
if (sceneType === 'after') {
afterTileset.value = tileset
} else {
beforeTileset.value = tileset
}
console.log(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型加载成功splitDirection: ${splitDirection}`)
// 只有明确要求时才自动缩放到tileset
if (autoZoom) {
await viewer.zoomTo(tileset)
}
return tileset
} catch (error) {
console.error(`[use3DTiles] 加载${sceneType === 'after' ? '灾后' : '灾前'}3D模型失败:`, error)
return null
}
}
/**
* 等待 Tileset 完全就绪(包括首批瓦片加载)
*
* 这个函数确保:
* 1. Tileset 本身已就绪readyPromise
* 2. 初始视图的所有瓦片已加载完成initialTilesLoaded
*
* @param {Cesium.Cesium3DTileset} tileset - 要等待的 Tileset
* @param {number} timeout - 超时时间毫秒默认10秒
* @returns {Promise<void>}
*/
const waitForTilesetReady = async (tileset, timeout = 10000) => {
if (!tileset) {
console.warn('[use3DTiles] waitForTilesetReady: tileset 为空')
return
}
try {
// 步骤1等待 Tileset 基础就绪
await tileset.readyPromise
console.log('[use3DTiles] Tileset readyPromise 已完成')
// 步骤2等待初始瓦片加载完成带超时
console.log('[use3DTiles] 等待初始瓦片加载...')
await Promise.race([
// 等待initialTilesLoaded事件
new Promise((resolve) => {
const handleInitialTilesLoaded = () => {
console.log('[use3DTiles] 初始瓦片加载完成(事件触发)')
tileset.initialTilesLoaded.removeEventListener(handleInitialTilesLoaded)
resolve()
}
tileset.initialTilesLoaded.addEventListener(handleInitialTilesLoaded)
// 如果已经加载完成可能事件已经触发过了直接resolve
// 通过检查tileset.tilesLoaded来判断
if (tileset.tilesLoaded) {
console.log('[use3DTiles] 瓦片已加载,直接继续')
tileset.initialTilesLoaded.removeEventListener(handleInitialTilesLoaded)
resolve()
}
}),
// 超时机制
new Promise((resolve) => {
setTimeout(() => {
console.warn(`[use3DTiles] 等待瓦片加载超时(${timeout}ms继续执行`)
resolve()
}, timeout)
})
])
console.log('[use3DTiles] Tileset 已完全就绪')
} catch (error) {
console.error('[use3DTiles] 等待 Tileset 就绪失败:', error)
// 即使失败也不抛出异常,允许程序继续执行
}
}
/**
* 移除3D Tileset
*/
const remove3DTileset = (viewer, sceneType) => {
if (!viewer) return
const tileset = sceneType === 'after' ? afterTileset.value : beforeTileset.value
if (tileset) {
viewer.scene.primitives.remove(tileset)
if (sceneType === 'after') {
afterTileset.value = null
} else {
beforeTileset.value = null
}
console.log(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型已移除`)
}
}
/**
* 更新 Tileset 的 splitDirection
* @param {string} sceneType - 场景类型:'before' 或 'after'
* @param {Cesium.SplitDirection} splitDirection - 新的分割方向
*/
const updateTilesetSplitDirection = (sceneType, splitDirection) => {
const tileset = sceneType === 'after' ? afterTileset.value : beforeTileset.value
if (tileset) {
tileset.splitDirection = splitDirection
console.log(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型 splitDirection 已更新为: ${splitDirection}`)
} else {
console.warn(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型不存在无法更新 splitDirection`)
}
}
return {
beforeTileset,
afterTileset,
load3DTileset,
waitForTilesetReady,
remove3DTileset,
updateTilesetSplitDirection
}
}
export default use3DTiles