基于鼠标位置缩放相机视口

前言

我们之前在建立相机轨道控制器的时候,滑动鼠标滚轮,相机会基于画布的中心点缩放视图。

后来有同学问我如何基于鼠标所在的位置缩放视图,我觉得这个功能可以有,所以就在此做个补充。

1-原理解析

基于鼠标所在的位置缩放视图时,缩放值正常写即可,但相机的位置会发生偏移。

效果如下:

因为相机会通过其位置的变化,确保鼠标所在的那个点不会在视口中发生位移,所以相机的位置就是此功能的关键点。

当我说相机的位置的时候,有基本功的同学就必然会想到坐标系。

咱们把坐标系的运算顺序再回顾一下:

canvas画布的像素坐标系 > 裁剪矩阵 > 相机视图矩阵 > 相机投影矩阵 > 模型矩阵

相机投影矩阵控制的是相机的缩放,当然这只是对于我们当前的二维相机而言的。

相机视图矩阵控制的是相机的位移和旋转,我们当前的二维相机里只有缩放,没有旋转。

我们刚才所说的相机位置对应的坐标系就是相机的视图矩阵。

接下来咱们解一个题,谋定而后动很适合如我这般不比学霸的人。

已知:

  • 鼠标在canvas画布上的像素位是点origin。
  • 相机的位置是camPos。

求:相机基于鼠标位置缩放s 后的相机位置。

解:

在解题时,首先要统一坐标系。

1.把鼠标点统一到相机位置所在的坐标系中,设此点为P1:

ini 复制代码
P1 = 裁剪矩阵 * origin + camPos

2.计算P1在基于默认的零点缩放s 后的位置,设此点为P2:

ini 复制代码
P2=P1*1/s

上面的1/s 是因为相机的缩放量与模型是反着的。

3.将相机的当前位置加上P2到P1的差值,便可确保鼠标所在的点位不动,周围的点位发生缩放。

makefile 复制代码
camPos+=P2-P1

思路已经清晰,接下来写代码就简单了。

2-代码

找到/src/lmm/controler/OrbitControler.ts 中的doScale() 方法。

之前我们是基于画布中心缩放视图的:

typescript 复制代码
doScale(deltaY: number) {
  const { enableZoom, camera, zoomSpeed } = this
  if (!enableZoom) {
    return
  }
  const scale = Math.pow(0.95, zoomSpeed)
  if (deltaY > 0) {
    camera.zoom /= scale
  } else {
    camera.zoom *= scale
  }
  this.dispatchEvent(_changeEvent)
} 

接下来我们对此方法做下扩展,使其可以基于裁剪坐标系里的某个点位进行缩放。

scss 复制代码
doScale(deltaY: number,origin?:Vector2) {
  const { enableZoom, camera, zoomSpeed } = this
  if (!enableZoom) {
    return
  }
  let scale = Math.pow(0.95, zoomSpeed)
  if (deltaY > 0) {
    scale=1/scale
  }
  camera.zoom *= scale
  if(origin){
    const P1=new Vector2().addVectors(origin,camera.position)
    const P2=P1.clone().multiplyScalar(1/scale)
    camera.position.add(P2.sub(P1))
  }
  this.dispatchEvent(_changeEvent)
}

doScale() 方法中的origin 参数就是裁剪坐标系里的某个点位,默认不写,就会基于画布中心缩放视图。

若origin 存在,那我们就计算出基点在视图矩阵中的点位P1,以及缩放后的点位P2,然后以两点之差偏移相机。

总结

这个功能的关键还是大家对于矩阵关系理解。

若你对其一知半解,那你就需要掌握解题的思路和步骤,一步步推导。

相关推荐
玩嵌入式的菜鸡22 分钟前
网页访问单片机设备---基于mqtt
前端·javascript·css
前端一小卒36 分钟前
我用 Claude Code 的 Superpowers 技能链写了个服务,部署前差点把服务器搞炸
前端·javascript·后端
滑雪的企鹅.2 小时前
HTML头部元信息避坑指南大纲
前端·html
一拳不是超人2 小时前
老婆天天吵吵要买塔罗牌,我直接用 AI 2 小时写了个在线塔罗牌
前端·ai编程
excel4 小时前
如何解决 Nuxt DevTools 中关于 unstorage 包的报错
前端
Rust研习社4 小时前
使用 Axum 构建高性能异步 Web 服务
开发语言·前端·网络·后端·http·rust
C澒4 小时前
AI 生码 - API2Code:接口智能匹配与 API 自动化生码全链路设计
前端·低代码·ai编程
浔川python社4 小时前
HTML头部元信息避坑指南技术文章大纲
前端·html
IT_陈寒4 小时前
SpringBoot配置加载顺序把我坑惨了
前端·人工智能·后端
kyriewen4 小时前
Next.js部署:从本地跑得欢,到线上飞得稳
前端·react.js·next.js