如视 Five SDK

Five.js

npm version

贝壳如视三维渲染框架

Five贝壳如视(realsee.com) 提供的在浏览器中运行的三维空间渲染 Javascript SDK。您可以通过如视开发者中心的数据服务,并结合Five,制作丰富多彩的三维空间应用。

并且Five提供了一系列的方法、事件、生命周期函数。您可以方便地基于Five进行二次开发,并结合到您自身的项目中,为您的项目添砖加瓦。

Five通过 TypeScript 编写,保证开发的质量以及编程体验,推荐通过 Visual Studio CodeWebStorm 等现代源代码编辑器,您将可以体验到友好的代码提示以及自动补全。

Five还提供了完善的 React Hooks API ,可以方便的通过 React 开发复杂的响应式数据应用。当然直接使用Five开发也是没有问题的。

Five提供通过 npm 的方式安装。Five基于 Three.js, 所以同时您需要安装相关依赖。

目前依赖的three版本为 115 ~ 117 的版本

npm install @realsee/five three@0.117.1

如果您使用React Hooks API,那么也请同时安装React的相关依赖。

npm install @realsee/five three@0.117.1 react react-dom @types/react @types/react-dom

接下来便可以在您的项目使用Five了。

import { Five } from "@realsee/five";
// 如果您使用 React Hoos API
import { useFiveState } from "@realsee/five/react";
Safari Safari on iOS Chrome Chrome for Android Edge Firefox
>= 9 >= 9 >= 49 >= 93 >= 13 >= 45

Five提供了快速上手体验的项目生成工具,您可以通过他熟悉Five的功能以及尝试基于Five开发功能。

  1. 先创建一个文件夹five-quick-start, 作为项目根目录并且使用 npm init 命令初始化一个工程。
mkdir five-quick-start && cd five-quick-start && npm init -y
  1. 安装Five
npm install @realsee/five
  1. 通过Five的内置的five-quick-start-init脚本来快速补完当前项目
npx five-quick-start-init

项目的文件结构如下

.
├── README.md
├── assets 静态文件测试数据
│ ├── data0.json
│ └── data1.json
├── index.html 页面模版
├── index.tsx 逻辑代码
├── package.json npm 包管理描述
├── tsconfig.json typescript 配置
├── webpack.config.js webpack 开发环境配置
└── webpack.production.js webpack 生产配置

npm script运行测试环境

npm run dev

默认将会在 port: 3000 开启 webpack dev server。您也可以在 webpack.config.js 中修改配置。

  1. 现在可以打来浏览器 http://0.0.0.0:3000 来看看项目初始化的效果了。您可以修改代码来体验一下如何使用Five来二次开发。

Five的简单使用样例:

import { Five } from "@realsee/five";

// 构造函数的具体参数见文档
const five = new Five();

// 将渲染视图的 canvas 添加到 DOM 中
five.appendTo(document.getElementById('app')!);

// 如果显示区域需要变动,在变动时请调用 refresh 重置显示参数
window.addEventListener('resize', () => five.refresh());

// 获取三维空间的 Work 数据,加载进来
// Work 数据可以通过如视开发者中心获取
fetch(`./work.json`)
.then(res => res.json())
.then(work => five.load(work));

其他的 Five API 待补充

React框架中使用Five的简单使用样例:

import * as React from "react";
import * as ReactDOM from "react-dom";
import {Five, Work, parseWork} from "@realsee/five";
import {createFiveProvider, FiveCanvas} from "@realsee/five/react";

// 创建 Provider, 参数与 new Five 参数类似
// 构造函数的具体参数见文档
const FiveProvider = createFiveProvider();

const App: React.FC = () => {

// 声明 State: Work
const [work, setWork] = React.useState<Work | null>(null);

// 声明 State: Size<{width, height}>
const [size, setSize] = React.useState({
width: window.innerWidth,
height: window.innerHeight
})

// 获取三维空间的 Work 数据,加载进来
const loadWork = React.useCallback((url: string) => {
fetch(url)
.then(res => res.json())
.then(data => setWork(parseWork(data)));
}, []);

React.useEffect(() => {
loadWork("./data0.json");
}, []);

// 如果显示区域需要变动,在变动时请调用 FiveCanvas 的 size
React.useEffect(() => {
const onResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
}
window.addEventListener("resize", onResize, false);
return () => window.removeEventListener("resize", onResize, false);
});

if (work) {
{/*
在 FiveProvider 内承载一个 Five 实例,
在他内部的组件可以使用 Five React Hook API 来获取/设置/操作 他
一个页面中也可以有多个 FiveProvider
*/}
return <FiveProvider initialWork={work}>
{/*
将渲染视图的 canvas 添加到 DOM 中
FiveCanvas 需要出现在 FiveProvider 内,他将渲染 FiveProvider 的视图
这样的设计可以实现不同的 DOM 层级结构
在 FiveProvider 内的其他组件也可以使用 Five React Hook API 来获取/设置/操作 他外部的 FiveProvider
*/}
<FiveCanvas width={size.width} height={size.height}/>
</FiveProvider>
}
return null;
}

ReactDOM.render(<App></App>, document.getElementById("app"));

其他的Five React Hook API文档建设中...

欢迎查看 Five API 文档

该文档由 TypeDoc 生成,您可以详细查看 api 使用方式,调用参数,数据结构。

Work如视开发者中心 提供的对于一个三维空间的描述。 是通过如视硬件设备(如视扫描仪如视Lite全景相机如视VR App )扫描并处理之后用于三维空间展示的数据。

WorkJSON作为数据格式Five框架可以解析Work数据并展示。一个Five实例每次可以载入并展示一个Work。并且也可以在不同的Work之间动态切换。

Work的数据样例如下

{
"initial": {
"mode": "Panorama",
"pano_index": 6,
"longitude": 2.6869287662553916,
"latitude": 0,
"fov": 95
},
"model": {
"file_url": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/model\/auto3d-DJaa08PIzN4JYluXQ1j2VS.at3d",
"material_textures": [
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_0.jpg",
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_1.jpg",
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_2.jpg",
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_3.jpg",
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_4.jpg",
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_5.jpg",
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_6.jpg",
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_7.jpg",
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_8.jpg",
"https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/materials\/texture_9.jpg"
]
},
"panorama": {
"list": [
{
"up": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/0\/2257f0f0b29d5b00ff01934ce51aaa35\/0_u.jpg",
"down": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/0\/2257f0f0b29d5b00ff01934ce51aaa35\/0_d.jpg",
"left": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/0\/2257f0f0b29d5b00ff01934ce51aaa35\/0_l.jpg",
"right": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/0\/2257f0f0b29d5b00ff01934ce51aaa35\/0_r.jpg",
"front": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/0\/2257f0f0b29d5b00ff01934ce51aaa35\/0_f.jpg",
"back": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/0\/2257f0f0b29d5b00ff01934ce51aaa35\/0_b.jpg"
},
{
"up": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/1\/ecb554bb1c122fa90186d176ccfecde4\/1_u.jpg",
"down": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/1\/ecb554bb1c122fa90186d176ccfecde4\/1_d.jpg",
"left": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/1\/ecb554bb1c122fa90186d176ccfecde4\/1_l.jpg",
"right": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/1\/ecb554bb1c122fa90186d176ccfecde4\/1_r.jpg",
"front": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/1\/ecb554bb1c122fa90186d176ccfecde4\/1_f.jpg",
"back": "https:\/\/vrlab-public.ljcdn.com\/release\/auto3dhd\/a62e1ebf7d013f7df117551a14af79fc\/images\/cube_2048\/1\/ecb554bb1c122fa90186d176ccfecde4\/1_b.jpg"
}
],
},
"observers": [
{
"visible_nodes": [ 1 ],
"accessible_nodes": [ 1 ],
"quaternion": {
"w": 0.45076583925142194,
"x": 0.010070951976936936,
"y": -0.8925839597148215,
"z": -0.0016154299986102319
},
"standing_position": [
-6.956049919128418,
-1.3924440682333898,
1.6591600179672241
],
"position": [
-6.956049919128418,
-0.10312400013208389,
1.6591600179672241
],
"floor_index": 0
},
{
"visible_nodes": [ 0 ],
"accessible_nodes": [ 0 ],
"index": 1,
"quaternion": {
"w": -0.9884643083591809,
"x": -0.0038900979633806664,
"y": 0.1512670435365699,
"z": -0.006439990839033269
},
"standing_position": [
-6.176340103149414,
-1.380554749576384,
2.179759979248047
],
"position": [
-6.176340103149414,
-0.10025200247764587,
2.179759979248047
],
"floor_index": 0,
}
]
}

Work 的数据说明

  • initial: 初始化数据,是一个 State 数据。描述Work被加载初始状态的位姿,也叫做VR的初始视角

    • mode: 模态
    • pano_index: 初始化点位
    • longitude: 相机的水平角
    • latitude: 相机的偏航角
    • fov: 相机垂直方向的可视角度
  • model: 三维模型

    • file_url: 三维模型的资源地址,文件为 .at3d 为如视定制的模型格式
    • material_textures: 三维模型的贴图资源地址
  • panorama: 全景彩色信息

    • list:
      • up / down / left / right / front / back: 全景彩色信息以 cubemap 方式存储和使用。
  • observers: 采集点信息

    • visible_nodes: 采集点之间的可见性列表
    • accessible_nodes: 采集点之间的连通性列表
    • quaternion: 采集点与模型坐标的旋转偏移量
    • standing_position: 采集点地面坐标
    • position: 采集点坐标
    • floor_index: 采集点楼层

StateFive框架用于描述状态的数据结构。他包含了模态、位于的采集点位、相机的方向、相机可视角度的信息。 您可以使用State来操作Five或者获取Five当前的状态。

interface State {
"mode": Five.Mode,
"panoIndex": number,
"longitude": number,
"latitude": number,
"fov": number,
"distanace": number,
"offset": THREE.Vector3,
}

State的数据描述

  • mode: 当前的模态 Five 常用有 5 种模态,可以使用 Five.Mode 获得

    • Panorama: 全景游走模态,该模态下视图将在采集点间游走,手势操作可以旋转/放大视角/切换采集点,适合查看采集的全景信息。
    • Floorplan: 空间总览模态, 该模态下视图以模型为中心,手势操作可以旋转/放大模型/切换楼层,适合查看模型的整体效果。
    • Topview: 户型图模态,该模态下视图以模型为中心,垂直俯视模型,手势操作可以平移/放大模型/切换楼层,适合查看模型平面结构。
    • Model: 模型游走模态,该模态下视图将在模型中自由游走,手势操作可以旋转/放大视角/位移,适合查看模型的细节,做一些定位操作。
    • Mapview: 地图模态, 该模态下视图将类似三维地图,手势操作可以旋转/放大视角/位移,适合查看模型的细节。适合展示小区,工厂等较大的模型。
    • VRPanorama: VR 眼镜模态,该模态下可以使用 Cardboard 眼镜 或者他的第三方衍生产品,实现 VR 虚拟显示效果。
    • XRPanorama: XR 眼镜模态,该模态下可以使用头戴显示设备比如 Pico, Meta Quest 等,实现沉浸式体验。
  • panoIndex: 采集点位

  • longitude / latitude: 相机的水平角 / 相机的偏航角(弧度),我们使用类似经纬度的方式描述相机位置。

  • fov: 相机垂直方向的可视角度 (角度)

  • distance: 相机距 offset 的距离

  • offset: 相机看向的目标

目前涉及到的模型类型 at3d / domez / 3d-tile(b3dm, pnts, glb)

  • world: five.scene 场景下的坐标。属于 view 下的绝对坐标。坐标的上方向是 Y 轴,单位米。
  • local: 单个模型的相对坐标。坐标的上方向是 Y 轴,单位米。 单个模型受到 work.transform 的影响会产生一定的位姿变化进行项目拼接(比如沙盘项目)。这种情况下,会与 world 有一定偏转。
  • enu: 模型描述坐标(East-North-Up)。坐标的上方向是 Z 轴,单位米。需要在算法模型输入/输出时使用这个坐标系。
  • ecef: 模型在地球的坐标系(Earth-centered, Earth-fixed)。坐标的上方向是 Z 轴并且固定是北,单位米。
  • lla: 模型在地球的经纬高度 (Latitude-Longitude-Altitude)。 经纬度单位是弧度,高度单位是米。

转换:

  • localworld 的转换依赖 work.transform
  • localenu 的转换是固定的 yUp 转 zUp
  • enuecef 依赖 tileset.jsonrootMeta.coordinate.pose_ecef_to_enu / rootMeta.coordinate.pose_enu_to_ecef。 如果缺损,则使用 a=6378137.0;invf=298.257223563 的椭球近似计算
  • lla 通过 a=6378137.0;invf=298.257223563 的椭球近似计算

椭球近似计算: (enu + lla) 与 ecef 可以相互推导。

  • 在 five 环境下,通过 viewLayer 对象,即 five.models[number].viewLayer[number].scene 转化。提供对应方法。
    • localToWorld(vector: THREE.Vector3): THREE.Vector3
    • worldToLocal(vector: THREE.Vector3): THREE.Vector3
    • localToEnu(vector: THREE.Vector3): THREE.Vector3
    • enuToLocal(vector: THREE.Vector3): THREE.Vector3
    • localToEcef(vector: THREE.Vector3): THREE.Vector3
    • ecefToLocal(vector: THREE.Vector3): THREE.Vector3
    • localToLla(vector: THREE.Vector3): THREE.Vector3
    • llaToLocal(vector: THREE.Vector3): THREE.Vector3
    • worldToEnu(vector: THREE.Vector3): THREE.Vector3
    • enuToWorld(vector: THREE.Vector3): THREE.Vector3
    • worldToEcef(vector: THREE.Vector3): THREE.Vector3
    • ecefToWorld(vector: THREE.Vector3): THREE.Vector3
    • worldToLla(vector: THREE.Vector3): THREE.Vector3
    • llaToWorld(vector: THREE.Vector3): THREE.Vector3

这些函数均为直接修改传入 vector。 如果要保持原先的数据,请实现 vector.clone()

通过 intersectRaycaster 方法得到的 Intersectionworld 坐标系下, 并返回对应的 modelviewLayer

目前约定为,算法的模型输入输出均使用 enu 坐标系。