如何用 deck.gl 的 FirstPersonView 配置第一人称相机视角
【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl
FirstPersonView是 deck.gl 提供的第一人称视角 View 类:相机被放置在指定的经纬度位置,朝向由viewState中的bearing(方位角)和pitch(俯仰角)决定,行为类似第一人称游戏。本文基于 deck.gl 官方文档,说明如何在应用中完成从安装、创建第一人称视角、配置相机参数到开启交互的完整操作路径,并给出多视图场景下的可选扩展方式。
准备条件:安装 deck.gl
第一人称视角需要的类都在@deck.gl/core中。安装完整框架:
npm install deck.gl --save或使用 yarn:
yarn add deck.gl如果你只用到核心渲染能力而不需要其他模块,也可以单独安装@deck.gl/core(以及所需的图层模块,如@deck.gl/layers)。注意各子模块版本需要手动保持同步。安装说明见 Getting Started。
官方建议在使用任何 View 类之前先阅读 Views and Projections 指南,理解View(相机固定配置)、viewState(运行时可变状态)和Viewport(实际的投影计算对象)三者的分工。
创建一个第一人称视角
最小可用的主路径:用Deck实例传入views: new FirstPersonView()、initialViewState和controller:
import {Deck, FirstPersonView} from '@deck.gl/core'; new Deck({ views: new FirstPersonView(), controller: {keyboard: false, inertia: true}, initialViewState: viewState });上面代码与把 controller 挂在 View 上等价,官方文档中两种写法均被展示:
import {Deck, FirstPersonView} from '@deck.gl/core'; new Deck({ views: new FirstPersonView({ controller: {keyboard: false, inertia: true} }), initialViewState: viewState });区别在于作用范围:Deck级的controller作用于整个 Deck,View级的controller只作用于该视图,多视图布局时更常用后者。
View State 参数
FirstPersonView需要搭配一个viewState对象渲染。它包含以下参数(来源:first-person-view.md):
| 参数 | 说明 | 默认值 |
|---|---|---|
longitude | 相机经度 | - |
latitude | 相机纬度 | - |
position | 相机相对经纬度锚点的米偏移(number[3]) | [0, 0, 0] |
bearing | 方位角,度 | 0(朝北) |
pitch | 俯仰角,度 | 0(水平) |
maxPitch | 最大俯仰角 | 90(正下方) |
minPitch | 最小俯仰角 | -90(正上方) |
仓库内的 360 视频示例 展示了一个完整的初始状态写法:
import type {FirstPersonViewState} from '@deck.gl/core'; const INITIAL_VIEW_STATE: FirstPersonViewState = { latitude: 0, longitude: 0, position: [0, 0, 0], pitch: 0, bearing: 90 };管理视角状态:stateful 与 stateless
根据 Views and Projections 指南:
- 只提供
initialViewState时,deck.gl 自动跟踪交互视图的 view state("stateful" 用法),这是最简单的路径。 - 如果需要在 deck.gl 之外管理视角状态(例如做"重置视角"按钮),改用
viewState属性,并监听onViewStateChange回调自行更新:
const deckInstance = new Deck({ viewState: INITIAL_VIEW_STATE, controller: true, onViewStateChange: e => { deckInstance.setProps({ viewState: e.viewState }); } }); document.getElementById('reset-btn').onclick = () => { deckInstance.setProps({ viewState: INITIAL_VIEW_STATE }); };React 项目中对应写法是使用useState保存 view state,并把onViewStateChange={e => setViewState(e.viewState)}传给<DeckGL />,参考 views.md 中的 React 示例。
调整相机参数
FirstPersonView接受与View基类相同的构造参数(id、x、y、width、height、controller等),另加投影相关参数(来源:first-person-view.md):
| 参数 | 说明 | 默认值 |
|---|---|---|
projectionMatrix | 投影矩阵(number[16]),提供后覆盖下方参数 | - |
fovy | 透视相机视野角,度 | 75 |
near | 近裁剪面距离 | 0.1 |
far | 远裁剪面距离 | 1000 |
focalDistance | 视口缩放修饰,对应每米像素数 | 1 |
注意near/far的默认远裁剪面是 1000 米单位距离,如果你的场景尺度更大(例如仓库中的车辆轨迹示例用到了coordinateSystem: 'meter-offsets'的图层),需要相应调大far。
x、y、width、height控制该视图在画布上的位置和大小,接受数字或 CSS 风格表达式(如'50%'、calc(50% - 10px)),默认铺满整个画布。multi-viewport 测试应用 中用height="50%"让第一人称视角只占上半屏:
<FirstPersonView id="firstPerson" controller={true} height="50%" fovy={50} />配置交互行为
默认情况下FirstPersonView使用FirstPersonController处理交互。开启方式:
const view = new FirstPersonView({id: 'pov', controller: true});FirstPersonController模拟人的移动方式(来源:first-person-controller.md):
- 滚轮:沿鼠标指针方向在水平 2D 平面上前进/后退(
scrollZoom); - 拖拽:
dragMode默认为'rotate',即拖拽旋转视角,shift+拖拽平移(dragPan默认true,v9.0 起支持); - 键盘:方向键移动相机,shift/ctrl+方向键旋转,
+/-垂直移动。
它支持全部 Controller 基类选项,例如inertia(拖拽后惯性,默认false,可传毫秒数)、keyboard(可传false关闭,或传对象调zoomSpeed、moveSpeed、rotateSpeedX/Y)、scrollZoom(可传对象调speed和smooth)、doubleClickZoom、maxBounds等。
如果视角不需要自由前进——比如 360 度视频示例 中相机固定在球心只能转头——就在 controller 选项里关闭移动相关能力:
<DeckGL views={new FirstPersonView()} initialViewState={INITIAL_VIEW_STATE} controller={{scrollZoom: false, doubleClickZoom: false}} ... />自定义 FirstPersonController
通过继承类可以覆盖事件处理逻辑,例如拦截pan事件:
import {Deck, FirstPersonView, FirstPersonController} from '@deck.gl/core'; class MyFirstPersonController extends FirstPersonController { handleEvent(event) { if (event.type === 'pan') { // do something } else { super.handleEvent(event); } } } new Deck({ views: new FirstPersonView(), controller: {type: MyFirstPersonController}, initialViewState: viewState })可覆盖的方法见 Controller 文档。
可选分支:第一人称视图 + 小地图双视图
deck.gl 支持多视图:views传一个View实例列表,每个视图有独立布局。一个典型场景是"车头第一人称视角 + 俯视小地图",官方示例用layerFilter保证汽车模型只在 minimap 中渲染(避免挡住第一人称镜头)(来源:views.md):
import {Deck, FirstPersonView, MapView} from '@deck.gl/core'; import {SimpleMeshLayer} from '@deck.gl/mesh-layers'; import {MVTLayer} from '@deck.gl/geo-layers'; const deckInstance = new Deck({ views: [ new FirstPersonView({id: 'first-person'}), new MapView({id: 'minimap', x: 10, y: 10, width: '20%', height: '20%'}) ], layerFilter: ({layer, viewport}) => { if (viewport.id === 'first-person' && layer.id === 'car') { // Do not draw the car layer in the first person view return false; } return true; } });多视图下viewState变为按视图 id 分键的对象,每个视图可独立或共享状态:
viewState: { 'first-person': { longitude: carPos.longitude, latitude: carPos.latitude, bearing: carPos.heading, position: [0, 0, 2] }, minimap: { longitude: carPos.longitude, latitude: carPos.latitude, zoom: 10 } }在onViewStateChange回调中通过viewId区分来自哪个视图的变化,从而实现"移动第一人称相机时小地图跟随"这类同步逻辑,multi-viewport 测试应用 给出了双向同步的完整实现。文档同时提醒:TileLayer、MVTLayer、HeatmapLayer、ScreenGridLayer等图层在视口变化时会执行昂贵的取数/聚合操作,一般不建议渲染进多个视图;确需这么做时为每个视图各建一个图层实例并用layerFilter限制其渲染范围。
如何判断配置是否生效
文档中没有给出统一的"成功日志",验证方式是按以下行为逐项核对:
- 初始位置:加载后相机应出现在
initialViewState指定的longitude/latitude(加position米偏移)处,朝向由bearing决定(0为朝北); - 交互:开启
controller后,拖拽应旋转视角(默认dragMode: 'rotate',shift+拖拽平移),滚轮应沿指针方向水平移动,方向键应移动相机——若这些行为没出现,检查controller是否被设为false/null,或选项里是否把对应能力(scrollZoom、keyboard等)关闭了; - 俯仰角限制:上下转头应在
minPitch(默认-90)到maxPitch(默认90)之间被约束; - 多视图:在
onViewStateChange中打印viewId和viewState,可确认哪个视图在产生状态变化,以及分键的 view state 是否按预期同步。
限制与注意事项
FirstPersonView属于 geospatial 视图,官方文档标注为 full support;不同 View 类使用的 view state 参数不同,配置前需查阅对应视图文档;FirstPersonController的dragPan默认true是 v9.0 起才支持的默认行为;- 若把 deck.gl 画布作为其他地图库(如 Mapbox/Google Maps)的 overlay,视口可能需要用那个库的 API 更新而非 deck.gl 的 API,详见 Views and Projections 中的说明;
- 相关 API 文档入口:FirstPersonView、FirstPersonController、View、Controller。
【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考