第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。croppedWidth 和 croppedHeight 是图像文件的实际像素尺寸。
图示:
┌──────────────────────────────────┐
│ 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 共享相同的适配器配置:baseBlur、showErrorTile、antialias。
支持多级 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 像素时,在移动设备上可能无法正常播放。适配器提供
shader和resolution配置项,含义与 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 性能优化要点
- 优先使用 panoData 而非修改原图:通过裁剪参数适配非标准比例图片,避免重新编码带来的质量损失。
- 合理使用分块适配器:分辨率超过 8000 像素时建议切换到 Tiles 适配器,用户体验提升显著。
- 设置低分辨率底图:Tiles 适配器的
baseUrl是关键的感知性能优化——确保首屏 200ms 内能看到模糊的全景图。 - 调整 resolution:如果全景图中包含大量直线(建筑等),适当提高
resolution可减少视觉变形。 - 利用缓存:多全景切换场景下保持
Cache.enabled = true,避免重复加载。
11. 本章小结
本章系统介绍了 Photo Sphere Viewer 的适配器体系:
- 适配器是 PSV 的核心抽象层,负责将不同格式的全景数据加载为 Three.js 可渲染的纹理。
- Equirectangular(默认) 是最通用的适配器,支持等距柱状投影图和裁剪全景图,内置
useXmpData、shader、resolution等配置。 - 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),学习如何在全景图中添加交互式热点标记。