Cesium倾斜摄影

Cesium倾斜摄影加载详细攻略

倾斜摄影加载流程图

获取倾斜摄影数据
配置3DTileset参数
加载倾斜摄影数据
调整位置和姿态
优化性能和渲染
添加交互和特效

一、倾斜摄影基础知识 ⭐⭐⭐

1. 什么是倾斜摄影

倾斜摄影是通过多角度、多方向拍摄的航拍技术,生成的三维模型能够真实反映地物的表面特征。在Cesium中,倾斜摄影数据通常以3D Tiles格式加载。

2. 3D Tiles格式说明

  • tileset.json: 描述数据的元数据文件
  • b3dm: 批量3D模型,最常见的格式
  • i3dm: 实例化3D模型
  • pnts: 点云格式
  • cmpt: 组合格式

二、倾斜摄影数据获取与准备 ⭐⭐

1. 数据来源

  • 商业平台:超图、Smart3D、ContextCapture等软件生成
  • 开源数据:部分城市开放数据可获取
  • 自建模型:使用无人机摄影+建模软件生成

2. 数据准备

项目目录结构示例:
/static/data/d3ties/
  ├── tileset.json     # 主配置文件
  ├── 0/               # LOD 0级瓦片
  │   ├── 0.b3dm
  │   ├── 1.b3dm
  │   └── ...
  ├── 1/               # LOD 1级瓦片
  │   ├── 0.b3dm
  │   └── ...
  └── ...

三、基本加载实现 ⭐⭐⭐

async function load3dTiles() {
  // 等待地形提供者准备完成
  await viewer.terrainProvider.readyPromise;
  
  // 创建并加载3D Tileset
  const tileset = await Cesium.Cesium3DTileset.fromUrl('/static/data/d3ties/tileset.json', {
    shadows: true,                 // 启用阴影
    preferLeaves: true,            // 优先加载叶节点
    maximumScreenSpaceError: 16,   // 屏幕空间误差(值越小越精细,性能消耗越大)
    maximumMemoryUsage: 2048       // 最大内存使用(MB)
  });
  
  // 添加到场景
  viewer.scene.primitives.add(tileset);
  
  // 视图定位到模型
  viewer.zoomTo(tileset);
}

四、详细配置参数解析 ⭐⭐⭐

完整的3D Tileset配置选项:

const tileset = await Cesium.Cesium3DTileset.fromUrl(url, {
  // 基础参数
  shadows: true,                    // 是否投射和接收阴影
  maximumScreenSpaceError: 16,      // 屏幕空间误差,控制LOD切换(小值 = 高质量)
  maximumMemoryUsage: 2048,         // 最大内存使用量(MB)
  
  // LOD策略参数
  skipLevelOfDetail: true,          // 启用跳过LOD级别,可提高性能
  baseScreenSpaceError: 1024,       // 基础SSE,高于此值时才应用skipLevels
  skipScreenSpaceErrorFactor: 16,   // 跳过LOD时的误差因子
  skipLevels: 2,                    // 跳过的LOD级别数
  
  // 预加载参数
  preloadWhenHidden: false,         // 当被遮挡时是否预加载
  preloadFlightDestinations: true,  // 当相机飞行时预加载目标区域
  preferLeaves: true,               // 优先加载叶节点
  
  // 动态LOD参数
  dynamicScreenSpaceError: true,    // 开启动态屏幕空间误差
  dynamicScreenSpaceErrorDensity: 0.00278,  // 密度参数
  dynamicScreenSpaceErrorFactor: 4.0,       // 动态因子
  dynamicScreenSpaceErrorHeightFalloff: 0.25, // 高度衰减系数
  
  // 细节参数
  maximumSSEDistance: 20000,        // 开始降低分辨率的最大距离
  debugShowContentBoundingVolume: false,  // 显示内容边界体(调试用)
  debugShowBoundingVolume: false,   // 显示边界体(调试用)
});

五、位置和形变调整 ⭐⭐

1. 模型矩阵调整 - 平移、旋转和缩放

// 获取模型中心点
const boundingSphere = tileset.boundingSphere;
const cartographicCenter = Cesium.Cartographic.fromCartesian(boundingSphere.center);

// 创建平移矩阵 - 高度调整
const surface = Cesium.Cartesian3.fromRadians(
  cartographicCenter.longitude, 
  cartographicCenter.latitude, 
  0  // 地表高度
);

const targetPosition = Cesium.Cartesian3.fromRadians(
  cartographicCenter.longitude, 
  cartographicCenter.latitude, 
  50  // 目标高度
);

// 计算偏移量
const translation = Cesium.Cartesian3.subtract(
  targetPosition, 
  surface, 
  new Cesium.Cartesian3()
);

// 应用平移变换
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);

// 如果需要旋转,可以组合多个变换
function createRotationMatrix(longitude, latitude, height, rotationRadians) {
  // 创建旋转矩阵
  const center = Cesium.Cartesian3.fromRadians(longitude, latitude, height);
  const rotationMatrix = Cesium.Matrix4.fromRotationZ(rotationRadians);
  
  // 平移到原点,应用旋转,再平移回去
  const translationToCenter = Cesium.Matrix4.fromTranslation(
    Cesium.Cartesian3.negate(center, new Cesium.Cartesian3())
  );
  const translationFromCenter = Cesium.Matrix4.fromTranslation(center);
  
  // 组合变换矩阵
  let modelMatrix = new Cesium.Matrix4();
  modelMatrix = Cesium.Matrix4.multiply(modelMatrix, translationFromCenter, modelMatrix);
  modelMatrix = Cesium.Matrix4.multiply(modelMatrix, rotationMatrix, modelMatrix);
  modelMatrix = Cesium.Matrix4.multiply(modelMatrix, translationToCenter, modelMatrix);
  
  return modelMatrix;
}

2. 根据经纬度调整位置

// 调整模型到特定经纬度
function positionTilesetAtLonLat(tileset, longitude, latitude, height) {
  // 计算从当前位置到目标位置的变换
  const cartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center);
  const surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0);
  const target = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
  const offset = Cesium.Cartesian3.subtract(target, surface, new Cesium.Cartesian3());
  
  // 应用变换
  tileset.modelMatrix = Cesium.Matrix4.fromTranslation(offset);
}

六、性能优化策略 ⭐⭐⭐

1. 内存和性能平衡参数

参数说明推荐值
maximumScreenSpaceError控制精度和性能的主要参数高性能: 32-64
平衡: 16
高质量: 4-8
maximumMemoryUsage内存使用上限(MB)2048-4096
dynamicScreenSpaceError远处细节降级true
skipLevelOfDetail跳过中间LODtrue
preferLeaves优先加载叶节点true(初次加载时)

2. 渐进式加载策略

// 渐进式加载策略
tileset.progressiveResolutionHeightFraction = 0.5; // 初始加载时使用较低分辨率

// 优先控制
viewer.scene.primitives.add(tileset);
// 设置加载优先级 (0为最高,数字越大优先级越低)
tileset.loadingPriority = 0; 

// 根据相机视图控制加载区域
tileset.cullRequestsWhileMoving = true;
tileset.cullRequestsWhileMovingMultiplier = 10; // 移动时减少请求的倍数

3. 大场景分块加载

对于超大场景,可采用分块加载策略:

// 城市不同区域独立加载
async function loadCityByDistricts() {
  const districts = [
    { name: '区域1', url: '/static/data/district1/tileset.json' },
    { name: '区域2', url: '/static/data/district2/tileset.json' },
    { name: '区域3', url: '/static/data/district3/tileset.json' }
  ];
  
  // 创建一个容器,用于管理所有tileset
  const tilesets = [];
  
  // 加载每个区域
  for (const district of districts) {
    const tileset = await Cesium.Cesium3DTileset.fromUrl(district.url, {
      maximumScreenSpaceError: 16,
      skipLevelOfDetail: true
    });
    
    // 根据相机距离设置不同的加载优先级
    tileset.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(0, 20000);
    
    viewer.scene.primitives.add(tileset);
    tilesets.push(tileset);
  }
  
  // 动态调整加载优先级
  viewer.scene.postRender.addEventListener(() => {
    const cameraPosition = viewer.camera.position;
    
    // 根据距离调整优先级
    tilesets.forEach(tileset => {
      const distance = Cesium.Cartesian3.distance(
        cameraPosition, 
        tileset.boundingSphere.center
      );
      
      // 距离越近,优先级越高
      tileset.loadingPriority = Math.floor(distance / 1000);
    });
  });
}

七、样式调整和特效 ⭐⭐

1. 基础样式调整

// 设置3D Tiles的样式
tileset.style = new Cesium.Cesium3DTileStyle({
  // 基于高度设置颜色
  color: {
    conditions: [
      ["${Height} >= 100", "color('blue')"],
      ["${Height} >= 50", "color('green')"],
      ["${Height} >= 25", "color('yellow')"],
      ["true", "color('red')"]
    ]
  },
  // 调整透明度
  show: "${Height} > 0",
  meta: {
    description: "'Building height: ' + ${Height} + ' meters'"
  }
});

2. 自定义着色器实现特效

// 使用自定义着色器实现轮廓发光效果
const customShader = new Cesium.CustomShader({
  vertexShaderText: /* GLSL */ `
    void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput) {
      // 法线外扩
      vsOutput.positionMC += normalize(vsInput.attributes.normalMC) * 0.05;
    }
  `,
  fragmentShaderText: /* GLSL */ `
    void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
      // 设置发光颜色
      material.diffuse = vec3(0.0, 1.0, 1.0); // 青色发光
      material.alpha = 0.6;
    }
  `,
  uniforms: {
    u_glowColor: {
      type: Cesium.UniformType.VEC3,
      value: [0.0, 1.0, 1.0]
    }
  }
});

// 应用着色器
tileset.customShader = customShader;

3. 动态效果

// 动态透明度效果
let alpha = 0.0;
let increasing = true;

viewer.scene.postUpdate.addEventListener(() => {
  // 调整透明度
  if (increasing) {
    alpha += 0.01;
    if (alpha >= 1.0) {
      alpha = 1.0;
      increasing = false;
    }
  } else {
    alpha -= 0.01;
    if (alpha <= 0.3) {
      alpha = 0.3;
      increasing = true;
    }
  }
  
  // 更新样式
  tileset.style = new Cesium.Cesium3DTileStyle({
    color: `rgba(255, 255, 255, ${alpha})`
  });
});

八、交互功能实现 ⭐⭐

1. 鼠标点选功能

// 添加点选事件
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function(click) {
  // 执行射线检测
  const pickedFeature = viewer.scene.pick(click.position);
  
  if (Cesium.defined(pickedFeature) && pickedFeature.primitive instanceof Cesium.Cesium3DTileset) {
    // 获取属性信息
    const properties = pickedFeature.getPropertyNames();
    let propertyHtml = '<table class="cesium-infoBox-defaultTable">';
    
    for (let i = 0; i < properties.length; i++) {
      const propertyName = properties[i];
      propertyHtml += `<tr><th>${propertyName}</th><td>${pickedFeature.getProperty(propertyName)}</td></tr>`;
    }
    propertyHtml += '</table>';
    
    // 显示信息框
    viewer.selectedEntity = new Cesium.Entity({
      name: '选中建筑',
      description: propertyHtml
    });
    
    // 高亮显示选中对象
    highlightFeature(pickedFeature);
  }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 高亮显示功能
function highlightFeature(feature) {
  // 恢复之前的样式
  if (previousHighlight) {
    previousHighlight.color = previousColor;
  }
  
  // 保存当前样式
  previousHighlight = feature;
  previousColor = feature.color.clone();
  
  // 设置高亮颜色
  feature.color = Cesium.Color.YELLOW.withAlpha(0.8);
}

2. 模型裁剪功能

// 添加裁剪平面
function addClippingPlanes(tileset) {
  // 获取模型中心点
  const center = tileset.boundingSphere.center;
  const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
  
  // 创建裁剪平面集合
  const clippingPlanes = new Cesium.ClippingPlaneCollection({
    planes: [
      new Cesium.ClippingPlane(new Cesium.Cartesian3(1.0, 0.0, 0.0), 0.0),  // 右侧裁剪面
      new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 1.0, 0.0), 0.0),  // 前侧裁剪面
      new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -1.0), 0.0)  // 顶部裁剪面
    ],
    edgeWidth: 1.0,
    edgeColor: Cesium.Color.WHITE,
    modelMatrix: transform
  });
  
  // 应用裁剪平面
  tileset.clippingPlanes = clippingPlanes;
  
  // 添加平面控制UI
  addClippingPlanesUI(clippingPlanes);
}

// 添加控制裁剪平面的UI
function addClippingPlanesUI(clippingPlanes) {
  // 创建控制滑块
  const xSlider = document.getElementById('xSlider');
  xSlider.addEventListener('input', function() {
    clippingPlanes.get(0).distance = Number(this.value);
  });
  
  // 类似添加y和z轴滑块...
}

九、常见问题和解决方案

1. 加载失败或显示不完整

// 检查加载错误并重试
tileset.readyPromise.then(function(tileset) {
  console.log('3D Tiles加载成功');
}).catch(function(error) {
  console.error('3D Tiles加载失败:', error);
  
  // 尝试使用备用数据源
  tryLoadBackupTileset();
});

// 设置超时重试
setTimeout(() => {
  if (!tileset.ready) {
    console.warn('3D Tiles加载超时,尝试重新加载');
    viewer.scene.primitives.remove(tileset);
    load3dTiles();
  }
}, 30000);  // 30秒超时

2. 性能问题

// 监控性能并自动调整
let frameRate = 0;
let frameCount = 0;
let lastTime = performance.now();

viewer.scene.postRender.addEventListener(() => {
  frameCount++;
  const now = performance.now();
  
  // 每秒计算帧率
  if (now - lastTime >= 1000) {
    frameRate = frameCount;
    frameCount = 0;
    lastTime = now;
    
    // 根据帧率动态调整质量
    if (frameRate < 20) {  // 帧率过低
      // 降低质量以提高性能
      tileset.maximumScreenSpaceError = Math.min(tileset.maximumScreenSpaceError * 1.5, 128);
      console.log(`帧率低(${frameRate}FPS),调整SSE为${tileset.maximumScreenSpaceError}`);
    } else if (frameRate > 40 && tileset.maximumScreenSpaceError > 8) {
      // 提高质量
      tileset.maximumScreenSpaceError = Math.max(tileset.maximumScreenSpaceError / 1.2, 8);
      console.log(`帧率高(${frameRate}FPS),调整SSE为${tileset.maximumScreenSpaceError}`);
    }
  }
});

3. 纹理模糊或变形

// 调整纹理质量
viewer.scene.postProcessStages.fxaa.enabled = true;  // 启用FXAA抗锯齿
viewer.scene.globe.maximumScreenSpaceError = 2;  // 提高地形精度

// 如果使用的是倾斜摄影,可以通过调整采样模式提高质量
tileset.pointCloudShading = new Cesium.PointCloudShading({
  attenuation: true,
  maximumAttenuation: 4
});

十、实用工具和调试技巧

1. 调试工具

// 显示3D Tiles边界框(调试用)
tileset.debugShowBoundingVolume = true;       // 显示边界体
tileset.debugShowContentBoundingVolume = true; // 显示内容边界体
tileset.debugShowViewerRequestVolume = true;   // 显示请求区域
tileset.debugShowGeometricError = true;        // 显示几何误差
tileset.debugShowRenderingStatistics = true;   // 显示渲染统计

// 添加调试面板
function addDebugPanel() {
  const debugPanel = document.createElement('div');
  debugPanel.className = 'cesium-debug-panel';
  debugPanel.innerHTML = `
    <h3>3D Tiles调试</h3>
    <label><input type="checkbox" id="debugBoundingVolume"> 显示边界体</label><br>
    <label><input type="checkbox" id="debugShowStatistics"> 显示统计</label><br>
    <label>最大屏幕空间误差: <input type="range" id="sseSlider" min="1" max="64" value="16"></label>
  `;
  document.body.appendChild(debugPanel);
  
  // 绑定事件
  document.getElementById('debugBoundingVolume').addEventListener('change', function() {
    tileset.debugShowBoundingVolume = this.checked;
  });
  
  document.getElementById('sseSlider').addEventListener('input', function() {
    tileset.maximumScreenSpaceError = Number(this.value);
    console.log(`SSE调整为: ${this.value}`);
  });
}

2. 实用测试和分析工具

// 显示加载统计信息
function showLoadingStatistics() {
  let totalTiles = 0;
  let loadedTiles = 0;
  let tilesProcessed = 0;
  
  const statsOverlay = document.createElement('div');
  statsOverlay.style.position = 'absolute';
  statsOverlay.style.top = '10px';
  statsOverlay.style.right = '10px';
  statsOverlay.style.padding = '10px';
  statsOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  statsOverlay.style.color = 'white';
  document.body.appendChild(statsOverlay);
  
  // 更新统计信息
  viewer.scene.postRender.addEventListener(() => {
    if (tileset.ready) {
      totalTiles = tileset.statistics.numberOfTilesTotal;
      loadedTiles = tileset.statistics.numberOfTilesWithContentReady;
      tilesProcessed = tileset.statistics.numberOfTilesProcessing;
      
      statsOverlay.innerHTML = `
        <div>总瓦片数: ${totalTiles}</div>
        <div>已加载: ${loadedTiles} (${Math.round(loadedTiles/totalTiles*100)}%)</div>
        <div>处理中: ${tilesProcessed}</div>
        <div>内存使用: ${Math.round(tileset.statistics.memorySizeInBytes / (1024 * 1024))}MB</div>
      `;
    }
  });
}

实战示例:完整的倾斜摄影加载与优化

async function loadOptimized3DTiles() {
  try {
    // 1. 创建带优化参数的3D Tileset
    const tileset = await Cesium.Cesium3DTileset.fromUrl('/static/data/d3ties/tileset.json', {
      shadows: true,
      maximumScreenSpaceError: 16,
      maximumMemoryUsage: 2048,
      skipLevelOfDetail: true,
      baseScreenSpaceError: 1024,
      skipScreenSpaceErrorFactor: 16,
      skipLevels: 1,
      preferLeaves: true,
      dynamicScreenSpaceError: true,
      dynamicScreenSpaceErrorDensity: 0.00278,
      dynamicScreenSpaceErrorFactor: 4.0,
      dynamicScreenSpaceErrorHeightFalloff: 0.25,
      progressiveResolutionHeightFraction: 0.5
    });
    
    // 2. 添加到场景
    viewer.scene.primitives.add(tileset);
    
    // 3. 高度调整
    const heightOffset = 0; // 实际项目中可能需要调整此值
    const boundingSphere = tileset.boundingSphere;
    const cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
    const surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0);
    const offset = Cesium.Cartesian3.fromRadians(
      cartographic.longitude,
      cartographic.latitude,
      heightOffset
    );
    const translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
    tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
    
    // 4. 初始样式设置
    tileset.style = new Cesium.Cesium3DTileStyle({
      color: "color('white')"
    });
    
    // 5. 飞行到模型
    viewer.flyTo(tileset, {
      duration: 3,
      offset: new Cesium.HeadingPitchRange(
        0.0,                             // 航向角
        Cesium.Math.toRadians(-30.0),    // 俯仰角
        tileset.boundingSphere.radius * 2.0  // 距离
      )
    });
    
    // 6. 添加性能监控
    setupPerformanceMonitoring(tileset);
    
    // 7. 添加交互功能
    setupTilesetInteraction(tileset);
    
    console.log('倾斜摄影模型加载成功');
    return tileset;
  } catch (error) {
    console.error('倾斜摄影模型加载失败:', error);
    showLoadingError();
  }
}

// 加载模型并开始使用
const tileset = await loadOptimized3DTiles();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端熊猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值