znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第04章:全景图类型与适配器详解

在前三章中我们完成了环境搭建、第一个全景应用以及 Viewer 核心配置与 API 的学习。从本章起,我们将深入 Photo Sphere Viewer 最关键的抽象层——适配器(Adapter)。适配器决定了”什么样的全景数据能被加载、以什么方式渲染”。理解适配器体系,是真正掌握 PSV 全景图处理能力的关键一步。

1. 适配器体系概述

1.1 什么是适配器

适配器是 Photo Sphere Viewer 架构中负责加载和渲染全景纹理的核心模块。每一类全景格式(等距柱状图、立方体贴图、视频、鱼眼等)对应一个独立的适配器类,该类封装了:

  • 纹理的加载逻辑(单文件 / 多文件 / 分块文件)
  • Three.js 中对应几何体的创建与纹理映射方式
  • 特定格式的坐标转换(像素坐标 ↔ 球面角度)

适配器在架构中的位置可以用以下层次表示:

用户代码 (Viewer 配置)
       │
       ▼
  ┌─────────┐
  │  Viewer  │  ← 事件调度、交互控制、插件管理
  └────┬─────┘
       │
       ▼
  ┌──────────────┐
  │    Adapter    │  ← 纹理加载、几何体创建、坐标转换
  └──────┬────────┘
         │
         ▼
  ┌──────────────┐
  │   Three.js    │  ← 底层 WebGL 渲染
  └──────────────┘

适配器通过 Viewer 构造函数的 adapter 选项指定。如果不指定,默认使用内置的 EquirectangularAdapter

1.2 完整适配器列表

v5.x 版本提供以下 7 种官方适配器:

适配器 npm 包名 说明
Equirectangular(默认) @photo-sphere-viewer/core 标准等距柱状投影全景图,已内置
Equirectangular Tiles @photo-sphere-viewer/equirectangular-tiles-adapter 分块加载高分辨率等距柱状投影图
Equirectangular Video @photo-sphere-viewer/equirectangular-video-adapter 360° 视频(等距柱状投影)
Cubemap @photo-sphere-viewer/cubemap-adapter 六面立体图(立方体贴图)
Cubemap Tiles @photo-sphere-viewer/cubemap-tiles-adapter 分块加载高分辨率立方体贴图
Cubemap Video @photo-sphere-viewer/cubemap-video-adapter 立方体格式 360° 视频
Dual Fisheye @photo-sphere-viewer/dual-fisheye-adapter 双鱼眼镜头原始文件

除 Equirectangular 外,其余适配器需要单独安装对应的 npm 包。所有适配器的导入与使用方式一致:

// 通用模式:导入适配器类 → 传给 Viewer 的 adapter 选项
import { CubemapAdapter } from '@photo-sphere-viewer/cubemap-adapter';

const viewer = new Viewer({
  container: 'viewer',
  adapter: CubemapAdapter,          // 指定适配器
  panorama: { /* 适配器特定的全景数据 */ }
});

部分适配器支持额外的配置项,可通过静态方法 AdaperClass.withConfig(config) 传入:

import { EquirectangularVideoAdapter } from '@photo-sphere-viewer/equirectangular-video-adapter';

const viewer = new Viewer({
  adapter: EquirectangularVideoAdapter.withConfig({
    autoplay: true,
    muted: true,
  }),
  panorama: { source: 'video.mp4' }
});

2. Equirectangular 适配器(默认)详解

2.1 等距柱状投影原理

等距柱状投影(Equirectangular Projection)是最常见、最简单的球面纹理映射方式。它将球面上的经度和纬度直接线性映射到平面图像的 X 和 Y 坐标上,因此宽高比必须为 2:1(360°:180°)。

绝大多数消费级 360° 相机(Ricoh Theta、Insta360 系列、GoPro Max、DJI 等)的标准输出格式就是等距柱状投影图,这使它成为最通用的全景格式。

2.2 基本使用

EquirectangularAdapter 已内置在 @photo-sphere-viewer/core 中,无需额外安装或显式声明。直接传入图片 URL 即可:

import { Viewer } from '@photo-sphere-viewer/core';

const viewer = new Viewer({
  container: 'viewer',
  panorama: 'https://example.com/panorama.jpg'
});

如需修改适配器配置,可显式声明并传入:

import { Viewer, EquirectangularAdapter } from '@photo-sphere-viewer/core';

const viewer = new Viewer({
  container: 'viewer',
  adapter: EquirectangularAdapter,
  panorama: 'https://example.com/panorama.jpg'
});

2.3 适配器专属配置

EquirectangularAdapter 支持三个特有配置项,通过 withConfig() 方法设置:

配置项 类型 默认值 说明
useXmpData boolean true 是否从图片 XMP 元数据中读取裁剪信息
shader boolean false 使用光线投射着色器渲染,可消除极点处的变形(将在未来版本默认开启)
resolution number 64 球体几何体的面数,值越大直线变形越小,但性能开销也越大
import { Viewer, EquirectangularAdapter } from '@photo-sphere-viewer/core';

const viewer = new Viewer({
  container: 'viewer',
  adapter: EquirectangularAdapter.withConfig({
    useXmpData: false,   // 不使用 XMP 数据
    shader: true,        // 启用着色器渲染,消除极点变形
    resolution: 96,      // 提高几何体面数以减少变形
  }),
  panorama: 'panorama.jpg'
});

resolution 的实际面数计算公式为 resolution² / 2。默认值 64 对应 2048 个面,提高到 96 则对应 4608 个面。

3. 裁剪全景图(Cropped Panorama)

3.1 为什么需要裁剪

并非所有全景图都覆盖完整的 360°×180° 球面。很多场景下全景图只覆盖部分区域,例如:

  • 360°×90° 的”环带”全景(水平完整覆盖,垂直仅覆盖赤道附近)
  • 180°×180° 的半球全景
  • 手机拍摄的”宽幅全景”(横向覆盖 180°~360°,纵向有限)

如果直接将这种非完整全景图当作完整球面来渲染,画面会被拉伸变形。Photo Sphere Viewer 通过 panoData 机制来正确处理这些裁剪全景图。

3.2 panoData 核心参数

panoData 对象包含六个关键字段:

参数 说明
fullWidth 完整全景图的逻辑宽度(必填)
fullHeight 完整全景图的逻辑高度(可选,默认 fullWidth / 2
croppedWidth 裁剪后实际图像宽度(可选,默认等于 fullWidth
croppedHeight 裁剪后实际图像高度(可选,默认等于 fullHeight
croppedX 裁剪区域左上角的 X 偏移量
croppedY 裁剪区域左上角的 Y 偏移量

fullWidth : fullHeight 的比例必须为 2:1。croppedWidthcroppedHeight 是图像文件的实际像素尺寸。

图示:

┌──────────────────────────────────┐
│          fullWidth (6000)        │
│  ┌───────────────────────┐      │
│  │                       │      │
│  │   croppedWidth=4000   │      │  fullHeight
│  │   croppedHeight=2000  │      │    (3000)
│  │                       │      │
│  │  croppedX=1000        │      │
│  │  croppedY=500         │      │
│  └───────────────────────┘      │
└──────────────────────────────────┘

3.3 三种裁剪数据提供方式

方式一:XMP 元数据(推荐)

如果全景图由 360° 相机或支持全景拼接的 App 生成,图片文件中通常已包含标准的 XMP 元数据。PSV 默认开启 useXmpData: true,会自动读取这些信息,无需任何额外配置。

XMP 数据遵循 Google Photo Sphere 规范:

<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <rdf:Description rdf:about=""
      xmlns:GPano="http://ns.google.com/photos/1.0/panorama/">
      <GPano:ProjectionType>equirectangular</GPano:ProjectionType>
      <GPano:FullPanoWidthPixels>6000</GPano:FullPanoWidthPixels>
      <GPano:FullPanoHeightPixels>3000</GPano:FullPanoHeightPixels>
      <GPano:CroppedAreaImageWidthPixels>4000</GPano:CroppedAreaImageWidthPixels>
      <GPano:CroppedAreaImageHeightPixels>2000</GPano:CroppedAreaImageHeightPixels>
      <GPano:CroppedAreaLeftPixels>1000</GPano:CroppedAreaLeftPixels>
      <GPano:CroppedAreaTopPixels>500</GPano:CroppedAreaTopPixels>
      <GPano:PoseHeadingDegrees>0</GPano:PoseHeadingDegrees>
      <GPano:PosePitchDegrees>0</GPano:PosePitchDegrees>
      <GPano:InitialViewHeadingDegrees>0</GPano:InitialViewHeadingDegrees>
      <GPano:InitialViewPitchDegrees>0</GPano:InitialViewPitchDegrees>
    </rdf:Description>
  </rdf:RDF>
</x:xmpmeta>
<?xpacket end="r"?>

如需手动写入 XMP 数据到图片,可使用 exiftool:

exiftool -tagsfromfile data.xmp -all:all panorama.jpg

方式二:手动传入 panoData

直接在 Viewer 配置中硬编码裁剪数据,适合没有 XMP 元数据的图片:

const viewer = new Viewer({
  container: 'viewer',
  panorama: 'partial_panorama.jpg',
  panoData: {
    fullWidth: 6000,
    fullHeight: 3000,      // 可选,默认 fullWidth / 2
    croppedWidth: 4000,    // 可选,默认等于 fullWidth
    croppedHeight: 2000,   // 可选,默认等于 fullHeight
    croppedX: 1000,
    croppedY: 500,
  }
});

方式三:动态 panoData 函数

当裁剪参数需要根据加载的图片动态计算时,传入一个函数:

const viewer = new Viewer({
  container: 'viewer',
  panorama: 'dynamic_pano.jpg',
  panoData: (image, xmpData) => {
    // image: 加载完成的 HTMLImageElement
    // xmpData: XMP 解析出的数据(如果有)
    // 返回一个 panoData 对象
    return {
      fullWidth: Math.max(image.width, image.height * 2),
      fullHeight: Math.round(image.width / 2),
      croppedWidth: image.width,
      croppedHeight: image.height,
      croppedX: 0,
      croppedY: Math.round((image.width / 2 - image.height) / 2),
    };
  }
});

3.4 默认行为

当图片不是 2:1 比例、没有 XMP 数据、也没有提供 panoData 时,PSV 会执行一个尽力而为的默认计算,尽可能无变形地显示图片:

// PSV 内部默认算法
const fullWidth = Math.max(img.width, img.height * 2);
const fullHeight = Math.round(fullWidth / 2);
const croppedX = Math.round((fullWidth - img.width) / 2);
const croppedY = Math.round((fullHeight - img.height) / 2);

这意味着即使你传入一张 3:1 或 1:1 的非标准比例图片,PSV 也不会直接崩溃,而是尝试用最佳方式渲染。

4. Cubemap 适配器详解

4.1 什么是立方体贴图

立方体贴图(Cubemap)将环境映射到以观察者为中心的立方体的六个面上。每个面覆盖 90°×90° 的视野,六张正方形图片分别对应:左、前、右、后、上、下

立方体贴图常见于:

  • 游戏引擎的环境反射贴图
  • 3D 渲染管线导出的全景
  • 需要精确控制各面分辨率的高质量全景

4.2 安装与基本使用

npm install @photo-sphere-viewer/cubemap-adapter
import { Viewer } from '@photo-sphere-viewer/core';
import { CubemapAdapter } from '@photo-sphere-viewer/cubemap-adapter';

const viewer = new Viewer({
  container: 'viewer',
  adapter: CubemapAdapter,
  panorama: {
    left:   'path/to/left.jpg',
    front:  'path/to/front.jpg',
    right:  'path/to/right.jpg',
    back:   'path/to/back.jpg',
    top:    'path/to/top.jpg',
    bottom: 'path/to/bottom.jpg',
  }
});

也可以使用数组形式,顺序必须为 left, front, right, back, top, bottom

panorama: [
  'path/to/left.jpg',
  'path/to/front.jpg',
  'path/to/right.jpg',
  'path/to/back.jpg',
  'path/to/top.jpg',
  'path/to/bottom.jpg',
]

4.3 Cubemap Adapter 的三种全景格式

CubemapAdapter 支持三种输入布局:

格式一:独立文件(Separate files)

最常见的形式,六个面各一个文件:

panorama: {
  type: 'separate',     // 可省略,这是默认值
  paths: {
    left:   'left.jpg',
    front:  'front.jpg',
    right:  'right.jpg',
    back:   'back.jpg',
    top:    'top.jpg',
    bottom: 'bottom.jpg',
  },
  flipTopBottom: false  // 如果上下面的方向不正确,设为 true
}

部分立方体贴图可能不需要完整的六个面,此时可将不需要的面设为 null

// 例如没有顶部和底部的半立方体全景
panorama: {
  left: 'left.jpg',
  front: 'front.jpg',
  right: 'right.jpg',
  back: 'back.jpg',
  top: null,
  bottom: null,
}

格式二:横向拼接条(Stripe)

所有六个面排列在一张横向长图中:

panorama: {
  type: 'stripe',
  path: 'cubemap_stripe.jpg',
  order: ['left', 'front', 'right', 'back', 'top', 'bottom'],  // 默认顺序
  flipTopBottom: false,
}

格式三:展开图(Polyhedron net / T 形布局)

六个面按 T 形展开在一张图中:

panorama: {
  type: 'net',
  path: 'cubemap_net.jpg',
}

4.4 立方体贴图中的坐标定位

使用 CubemapAdapter 时,像素坐标需要额外指定 textureFace 字段来标识面:

// 在前面的某个像素位置放置标记
viewer.rotate({
  textureFace: 'front',
  textureX: 200,
  textureY: 800,
});

这与等距柱状投影中只需 textureX / textureY 的做法不同,是使用立方体贴图时需要特别注意的地方。

5. Equirectangular Tiles 适配器

5.1 分块加载原理

当全景图分辨率极高(如 12000×6000 甚至更大)时,直接加载整张图片会导致首屏等待时间过长。Equirectangular Tiles 适配器将大图按网格切成多个小瓦片(tiles),只加载当前视角可见区域的瓦片,显著降低初始加载时间和带宽消耗。

5.2 安装与配置

npm install @photo-sphere-viewer/equirectangular-tiles-adapter
import { Viewer } from '@photo-sphere-viewer/core';
import { EquirectangularTilesAdapter } from '@photo-sphere-viewer/equirectangular-tiles-adapter';

const viewer = new Viewer({
  container: 'viewer',
  adapter: EquirectangularTilesAdapter,
  panorama: {
    width: 12000,      // 完整全景图宽度
    cols: 16,          // 水平切分列数
    rows: 8,           // 垂直切分行数
    baseUrl: 'panorama_low.jpg',  // 低分辨率底图,快速加载
    tileUrl: (col, row) => {
      return `tiles/${col}_${row}.jpg`;
    },
  }
});

5.3 多级分辨率

Equirectangular Tiles 适配器支持多级 LOD(Level of Detail),可在不同缩放级别提供不同分辨率的瓦片集合,实现”拉近看高清、拉远看低清”的效果:

panorama: {
  // 最低级别(缩小时使用)
  baseUrl: 'panorama_low.jpg',
  // 默认瓦片配置
  width: 12000,
  cols: 16,
  rows: 8,
  tileUrl: (col, row) => `tiles/${col}_${row}.jpg`,
  // 多级配置:数组按缩入深度排列
  levels: [
    {
      // 第一级:稍微放大时使用
      width: 24000,
      cols: 32,
      rows: 16,
      tileUrl: (col, row) => `tiles_hd/${col}_${row}.jpg`,
    },
    {
      // 第二级:最大放大时使用
      width: 48000,
      cols: 64,
      rows: 32,
      tileUrl: (col, row) => `tiles_uhd/${col}_${row}.jpg`,
    },
  ],
}

5.4 适配器专属配置

配置项 类型 默认值 说明
baseBlur boolean true 对低分辨率底图应用模糊滤镜,避免马赛克感
showErrorTile boolean true 加载失败的瓦片显示警告图标
antialias boolean true 高分辨率瓦片启用抗锯齿
const viewer = new Viewer({
  adapter: EquirectangularTilesAdapter.withConfig({
    baseBlur: true,
    showErrorTile: false,
    antialias: false,
  }),
  // ...
});

5.5 准备瓦片

使用 ImageMagick 将全景图切成瓦片。假设原图 12000×6000,每片 750×750(16×8 网格):

magick.exe panorama.jpg \
  -crop 750x750 -quality 95 \
  -set filename:tile "%[fx:page.x/750]_%[fx:page.y/750]" \
  -set filename:orig %t \
  %[filename:orig]_%[filename:tile].jpg

性能建议:单个瓦片建议不超过 1024×1024 像素。对于 32 列、16 行的配置,理论上可支持高达 65536×32768(约 2 Gigapixels)的全景图。

6. Cubemap Tiles 适配器

6.1 原理与场景

与 Equirectangular Tiles 类似,Cubemap Tiles 将立方体的每个面切成网格瓦片,按需加载。适合超高分辨率的立方体贴图全景。

6.2 安装与使用

npm install @photo-sphere-viewer/cubemap-tiles-adapter
import { Viewer } from '@photo-sphere-viewer/core';
import { CubemapTilesAdapter } from '@photo-sphere-viewer/cubemap-tiles-adapter';

const viewer = new Viewer({
  container: 'viewer',
  adapter: CubemapTilesAdapter,
  panorama: {
    faceSize: 6000,     // 每个面的边长(像素)
    nbTiles: 8,         // 每个面切分为 nbTiles × nbTiles 个瓦片
    baseUrl: {
      left:   'left_low.jpg',
      front:  'front_low.jpg',
      right:  'right_low.jpg',
      back:   'back_low.jpg',
      top:    'top_low.jpg',
      bottom: 'bottom_low.jpg',
    },
    tileUrl: (face, col, row) => {
      return `${face}_${col}_${row}.jpg`;
    },
  }
});

6.3 配置项

与 Equirectangular Tiles 共享相同的适配器配置:baseBlurshowErrorTileantialias

支持多级 LOD 配置,与 Equirectangular Tiles 的 levels 数组模式一致。

用 ImageMagick 准备瓦片的方法与 Equirectangular 相同,只需对每个面分别执行裁剪命令。

性能上限:建议每面不超过 16384×16384 像素,对应的单个瓦片大小不超过 1024×1024。总计约 1.6 Gigapixels。

7. 视频适配器

Photo Sphere Viewer v5.x 提供两种视频适配器,它们都依赖 VideoPlugin 来实现播放控制。

7.1 Equirectangular Video 适配器

用于加载等距柱状投影格式的 360° 视频文件。

npm install @photo-sphere-viewer/equirectangular-video-adapter
import { Viewer } from '@photo-sphere-viewer/core';
import { EquirectangularVideoAdapter } from '@photo-sphere-viewer/equirectangular-video-adapter';
import { VideoPlugin } from '@photo-sphere-viewer/video-plugin';

const viewer = new Viewer({
  container: 'viewer',
  adapter: EquirectangularVideoAdapter.withConfig({
    autoplay: true,    // 自动播放
    muted: true,       // 静音(autoplay 通常需要 muted)
  }),
  panorama: {
    source: 'path/to/360_video.mp4',
  },
  plugins: [VideoPlugin],
});

panorama.source 支持三种形式:

// 1. 视频文件 URL
panorama: { source: 'video.mp4' }

// 2. MediaStream(USB 摄像头输入)
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
panorama: { source: stream }

// 3. 已有 HTMLVideoElement
const videoEl = document.querySelector('video');
panorama: { source: videoEl }

视频也可以是裁剪全景(非完整球面),通过 panorama.data 提供裁剪信息:

panorama: {
  source: 'partial_video.mp4',
  data: {
    fullWidth: 6000,
    croppedX: 1000,
    croppedY: 500,
  }
}

注意:视频分辨率超过 4096 像素时,在移动设备上可能无法正常播放。适配器提供 shaderresolution 配置项,含义与 EquirectangularAdapter 相同。

7.2 Cubemap Video 适配器

用于加载立方体布局的 360° 视频,如 YouTube 使用的 EAC(Equi-Angular Cubemap)格式。

npm install @photo-sphere-viewer/cubemap-video-adapter
import { Viewer } from '@photo-sphere-viewer/core';
import { CubemapVideoAdapter } from '@photo-sphere-viewer/cubemap-video-adapter';
import { VideoPlugin } from '@photo-sphere-viewer/video-plugin';

const viewer = new Viewer({
  container: 'viewer',
  adapter: CubemapVideoAdapter.withConfig({
    autoplay: true,
    muted: true,
  }),
  panorama: {
    source: 'path/to/cubemap_video.mp4',
    equiangular: true,  // 使用 EAC 格式,YouTube 标准
  },
  plugins: [VideoPlugin],
});

Cubemap 视频的画面布局为六面网格排列:

┌──────┬──────┬──────┐
│ 右   │ 左   │ 上   │
├──────┼──────┼──────┤
│ 下   │ 前   │ 后   │
└──────┴──────┴──────┘

EAC(equiangular: true)提供比普通立方体贴图更均匀的像素分布,是 YouTube 360° 视频的标准格式。如果视频使用标准立方体贴图,设为 false

8. Dual Fisheye 适配器

8.1 双鱼眼镜头

“Dual Fisheye” 是很多 360° 相机的原始文件格式——两个圆形鱼眼图像左右并排,每个覆盖约 180° 的视场。相机配套软件通常会将双鱼眼图拼接为等距柱状投影,但 PSV 的 Dual Fisheye 适配器可以直接渲染原始文件,省去中间转换步骤。

npm install @photo-sphere-viewer/dual-fisheye-adapter
import { Viewer } from '@photo-sphere-viewer/core';
import { DualFisheyeAdapter } from '@photo-sphere-viewer/dual-fisheye-adapter';

const viewer = new Viewer({
  container: 'viewer',
  adapter: DualFisheyeAdapter,
  panorama: 'dual_fisheye_image.jpg',
});

8.2 注意事项

该适配器目前主要为 Ricoh Theta Z1 的原始文件测试和调校,其他品牌相机(如 Insta360 部分型号、Samsung Gear 360 等)的兼容性取决于具体的镜头参数。如果遇到渲染偏差,可以向官方仓库提交 issue 并提供示例文件。

视频文件需要先将左右两个 insv 视频流水平拼接为单个视频。使用 ffmpeg:

ffmpeg -i input_left.insv -i input_right.insv \
    -filter_complex "[0:v][1:v]hstack=inputs=2,scale=4096:-1,vflip[v];[0:a][1:a]amerge=inputs=2[a]" \
    -map "[v]" -map "[a]" \
    -c:v libsvtav1 -crf 25 \
    output.mp4

9. 过渡动画(Transitions)

9.1 默认过渡效果

当使用 setPanorama() 切换全景图时,PSV 默认启用过渡动画。可以通过 defaultTransition 选项全局配置,也可以在每次 setPanorama() 调用中覆盖。

全局配置:

const viewer = new Viewer({
  container: 'viewer',
  panorama: 'image01.jpg',
  defaultTransition: {
    speed: 1500,       // 动画时长(毫秒)
    rotation: true,    // 是否旋转到新全景的初始视角
    effect: 'fade',    // 过渡效果类型
  }
});

单次切换覆盖:

viewer.setPanorama('image02.jpg', {
  transition: {
    speed: 800,
    rotation: false,
    effect: 'black',
  }
}).then(() => {
  console.log('新全景图加载完成');
});

完全禁用过渡:

viewer.setPanorama('image02.jpg', { transition: false });

9.2 speed 参数

speed 可以是数字(毫秒)或包含转速的字符串:

// 1500 毫秒
speed: 1500

// 以每分钟 2 转的速度旋转(动画时长取决于旋转角度)
speed: '2rpm'

9.3 支持的效果

effect 值 效果描述
'fade' 淡入淡出(默认)
'black' 先黑场再淡入
'white' 先白场再淡入
// 一个完整的过渡配置示例
viewer.setPanorama('image03.jpg', {
  caption: '新的全景图',
  position: { yaw: '90deg', pitch: '-20deg' },
  transition: {
    speed: 1200,
    rotation: true,
    effect: 'fade',
  },
  sphereCorrection: { pan: '5deg', tilt: 0, roll: 0 },
});

9.4 全景图缓存

PSV 内置了全局缓存系统,切换全景图时会保留之前加载的纹理,以便快速切回。可通过导入 Cache 对象来配置:

import { Cache } from '@photo-sphere-viewer/core';

Cache.enabled = true;       // 是否启用缓存(默认 true)
Cache.ttl = 600;            // 保留时长(分钟,默认 600)
Cache.maxItems = 10;        // 最大缓存数量(默认 10)

10. 最佳实践与选型指南

10.1 按输入格式选择适配器

你的全景数据是什么格式?
│
├─ JPEG/PNG/WebP 图片
│   ├─ 单张 2:1 图片 → Equirectangular(默认)
│   ├─ 单张非 2:1 图片 → Equirectangular + panoData
│   ├─ 6 张正方形图片 → Cubemap
│   └─ 极高分辨率 (>10K) → Equirectangular Tiles / Cubemap Tiles
│
├─ 视频文件
│   ├─ 2:1 比例 → Equirectangular Video + VideoPlugin
│   └─ 六面网格布局 → Cubemap Video + VideoPlugin
│
└─ 相机原始文件
    └─ 左右双鱼眼图 → Dual Fisheye

10.2 各适配器性能对比

适配器 首次加载速度 运行时性能 内存占用 适用分辨率上限
Equirectangular 中等 中等 ~8K
Equirectangular Tiles 快(底图) 较低 ~65K(2 Gigapixels)
Equirectangular Video 取决于视频 中等 ~4K(移动端限制)
Cubemap 较慢(6文件) 较高 ~8K/面
Cubemap Tiles 快(底图) 较低 ~16K/面(1.6 Gigapixels)
Cubemap Video 取决于视频 中等 ~4K
Dual Fisheye 中等 中等 取决于相机

10.3 推荐组合

通用全景展示(最常见场景)

// 使用默认 Equirectangular + panoData 即可满足大多数需求
new Viewer({
  container: 'viewer',
  panorama: 'panorama.jpg',
  panoData: { fullWidth: 6000, croppedWidth: 4000, croppedX: 1000, croppedY: 500 }
});

超高分辨率全景漫游

// 分块加载 + 多级 LOD,支持极限分辨率
new Viewer({
  container: 'viewer',
  adapter: EquirectangularTilesAdapter.withConfig({ baseBlur: true }),
  panorama: {
    width: 24000, cols: 32, rows: 16,
    baseUrl: 'low.jpg',
    tileUrl: (col, row) => `tiles/${col}_${row}.jpg`,
    levels: [{
      width: 48000, cols: 64, rows: 32,
      tileUrl: (col, row) => `tiles_hd/${col}_${row}.jpg`,
    }]
  }
});

游戏引擎导出全景

// 使用 Cubemap 适配器,直接加载游戏标准格式
new Viewer({
  container: 'viewer',
  adapter: CubemapAdapter,
  panorama: { type: 'stripe', path: 'skybox.jpg' }
});

360° 视频播放

// 视频适配器必须配合 VideoPlugin
new Viewer({
  container: 'viewer',
  adapter: EquirectangularVideoAdapter.withConfig({ autoplay: true, muted: true }),
  panorama: { source: '360_video.mp4' },
  plugins: [VideoPlugin]
});

10.4 性能优化要点

  1. 优先使用 panoData 而非修改原图:通过裁剪参数适配非标准比例图片,避免重新编码带来的质量损失。
  2. 合理使用分块适配器:分辨率超过 8000 像素时建议切换到 Tiles 适配器,用户体验提升显著。
  3. 设置低分辨率底图:Tiles 适配器的 baseUrl 是关键的感知性能优化——确保首屏 200ms 内能看到模糊的全景图。
  4. 调整 resolution:如果全景图中包含大量直线(建筑等),适当提高 resolution 可减少视觉变形。
  5. 利用缓存:多全景切换场景下保持 Cache.enabled = true,避免重复加载。

11. 本章小结

本章系统介绍了 Photo Sphere Viewer 的适配器体系:

  • 适配器是 PSV 的核心抽象层,负责将不同格式的全景数据加载为 Three.js 可渲染的纹理。
  • Equirectangular(默认) 是最通用的适配器,支持等距柱状投影图和裁剪全景图,内置 useXmpDatashaderresolution 等配置。
  • panoData 机制 是处理非标准比例全景图的核心工具,支持 XMP 自动读取、手动配置和动态函数三种模式。
  • Cubemap 适配器用于立方体贴图格式,支持独立文件、横向拼接和 T 形布局三种输入形式。
  • Tiles 适配器(Equirectangular Tiles 和 Cubemap Tiles)通过分块加载和多级 LOD 支持高达 Gigapixel 级别的超高分辨率全景图。
  • 视频适配器 依赖 VideoPlugin,支持等距柱状和立方体两种视频格式,以及摄像头流的实时输入。
  • Dual Fisheye 适配器可直接渲染双鱼眼相机的原始文件。
  • 过渡动画 支持 fade / black / white 三种效果,可配置速度和旋转行为。
  • 选型策略:大多数场景使用默认 Equirectangular;高分辨率使用 Tiles;视频使用 Video;立方体贴图使用 Cubemap。

下一章我们将深入 Photo Sphere Viewer 最强大的功能之一——标记系统(MarkersPlugin),学习如何在全景图中添加交互式热点标记。


← 上一章 下一章 →