4.2 轨道控制与运镜:让相机动起来


文档摘要

4.2 轨道控制与运镜:让相机动起来 本节摘要:轨道控制把相机锁在以目标点为球心的球面上,拖拽即公转、滚轮即自转、阻尼给滑行感。本节讲清球面模型的参数含义与两套手感配置,再用缓动手法实现自动巡展与一键特写,收束机位工位并为第 6 章动画系统热身。 云台的球面模型 OrbitControls 的本质是一个球面约束器:相机被放在以目标点(默认原点)为球心、当前距离为半径的球面上,鼠标拖拽改变的是球面上的经纬角(方位角与俯仰角),滚轮改变的是球半径。

4.2 轨道控制与运镜:让相机动起来

本节摘要:轨道控制把相机锁在以目标点为球心的球面上,拖拽即公转、滚轮即自转、阻尼给滑行感。本节讲清球面模型的参数含义与两套手感配置,再用缓动手法实现自动巡展与一键特写,收束机位工位并为第 6 章动画系统热身。

云台的球面模型

OrbitControls 的本质是一个球面约束器:相机被放在以目标点(默认原点)为球心、当前距离为半径的球面上,鼠标拖拽改变的是球面上的经纬角(方位角与俯仰角),滚轮改变的是球半径。理解了这个模型,控制器的所有参数突然都有了画面:minDistance 与 maxDistance 是球壳的内外壁,minPolarAngle 与 maxPolarAngle 是纬度的天花板与地板,enableDamping 加 dampingFactor 则是给相机装了一副液压云台——每次拖拽后相机带着惯性滑行一小段,这就是"跟手"与"生硬"的分水岭。

必须强调一句纪律:启用阻尼后,controls.update() 必须每帧调用(1.2 的工装底座已内置),否则阻尼不生效、拖拽手感退回出厂设置——这是轨道控制报障榜的第一名,症状是"我明明开了阻尼"。

图:轨道控制的球面约束与可调手感

图:轨道控制的球面约束与可调手感

动手:一套云台,两种手感,一段运镜

背景:沙盘要上评审大屏:默认状态下缓慢自动旋转供远观,讲解员点击货箱后机位平滑推近到该货箱特写,点击空白处拉回全景。要求全程无瞬间跳切。

操作:先配"展示飘"手感,再用每帧插值实现一键特写——运镜的本质是把"目标机位"逐帧逼近,而不是一步赋值。

import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; const controls = new OrbitControls(stage.camera, stage.renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.08; // 展示飘:惯性略长;工具稳取 0.05 controls.minDistance = 3; // 球壳内壁:再近就穿模 controls.maxDistance = 18; // 外壁:再远就看不清 controls.maxPolarAngle = Math.PI / 2.05; // 纬度地板:略高于水平面,防止钻到地下 // 运镜状态机:空 = 无任务;moving = 正在推轨 let flyTo = null; // 一键特写:传入目标点与期望距离 function focusOn(pos, distance = 2.5) { flyTo = { target: pos.clone(), // 要看的点 camPos: pos.clone().add(new THREE.Vector3(1.2, 1.0, 1.2) .normalize().multiplyScalar(distance)), // 机位落在目标的斜前上方 t: 0 // 插值进度 0 到 1 }; } function resetView() { flyTo = { target: new THREE.Vector3(0, 0.5, 0), camPos: new THREE.Vector3(6, 5, 8), t: 0 }; } // 每帧:先处理运镜,再做常规更新 stage.loop((dt) => { if (flyTo) { flyTo.t = Math.min(flyTo.t + dt * 1.5, 1); const k = 1 - Math.pow(1 - flyTo.t, 3); // easeOutCubic:快起步缓收尾 controls.target.lerp(flyTo.target, k * 0.2); // 目标点逐帧逼近 stage.camera.position.lerp(flyTo.camPos, k * 0.2); if (flyTo.t >= 1) flyTo = null; // 到站交还控制权 } controls.update(); stage.renderer.render(stage.scene, stage.camera); });

结果:默认状态下沙盘徐徐自转(把每帧给 controls 的方位角加一个小常量即可叠加自动巡展);点击高货箱,机位两秒内滑到斜前上方特写,缓出收尾没有急刹车;点击空白处,全景机位原路滑回。讲解员全程拖拽不受影响——运镜只在飞行途中接管,到站即还权。

解读:三处细节撑起了"语言感"。第一,lerp 的系数乘了 0.2 而不是直接用进度值:逐帧向"已被进度推进的目标"逼近,机位轨迹是平滑曲线而非折线——这是插值运镜的通用技巧,目标变了也不跳变。第二,easeOutCubic 让推轨"快起步、缓进站",比线性插值自然得多;各类缓动函数第 6 章会系统盘点,这里先混个脸熟。第三,polar 角上限取 2.05 分之 pi 而不是 2 分之 pi:留一线俯角,云台永远不至于与地面平行导致贴地穿模——限位参数留余量,是交互配置的通用美德。

变式:自动巡展与用户操作打架怎么办?监听 controls 的 start 事件,用户一拖拽就停掉自动旋转、若干秒无操作后再淡入恢复——"idle 自动巡展"是展示类页面的标准交互范式。再进阶:把巡展路径做成关键帧列表(多个机位点位轮流飞),就是最简版的产品漫游导览,第 6 章动画系统里这套思路会升级成正式的剪辑与混合。

平移、目标点与"轴心"的讲究

轨道控制还有第三种手势:右键拖拽平移(enablePan),它移动的是相机与目标点这对"连体件"。要留意的细节在轴心:展示类页面几乎都该锁定 target 到主体中心(比如货盘中心抬半高),平移功能可以直接关掉——用户平移一激动把主体挪出画面,比不能平移糟糕得多。工具类页面才保留平移,并配 panSpeed 调速。另一个常被忽略的参数是 zoomSpeed 与旋转速度(rotateSpeed):触屏双指缩放与滚轮缩放共用它,大场景给 0.8 到 1.2、小物件场景给 0.5 左右,手感立刻收敛。所有速度参数没有标准值,评审机上让非开发者拖十分钟,他们的手比参数表诚实。

本节要点回顾

  • 球面模型三个自由度:经纬角给拖拽、半径给缩放、目标点是球心,参数全部有画面;
  • 阻尼必须每帧 update:开阻尼不调 update 等于没开,报障榜第一名;
  • 限位留余量:polar 上限略高于水平、缩放范围贴需求,云台永远不穿模;
  • 运镜写插值不写赋值:lerp 逼近目标点,缓动收尾,到站交还控制权;
  • idle 自动巡展:监听交互事件暂停与恢复自动旋转,展示页标准范式。

机位工位收工,观看方式已齐。下一章外协件进场:设计师的真实模型如何过五关进厂——格式、加载器、验收,一道都不能省。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U