文章目录

  • 屏幕坐标系和标准设备坐标
  • 屏幕坐标转世界坐标
  • 世界坐标转屏幕坐标

  要理解坐标系间的转换过程,需要提前了解:

  1. ThreeJS 中的几种坐标系
  2. 屏幕坐标系和标准设备坐标系

  不想看链接中的内容这边也有不规范的简述

  物体的坐标转换过程大致为:局部坐标 -> 世界坐标 -> 观察空间坐标 -> 裁剪空间坐标 -> 屏幕空间坐标

  我们将 观察空间坐标系裁剪空间坐标系 之间的转换统一处理,最终得到 标准设备坐标系

  因此坐标转换过程就变成了:局部坐标 -> 世界坐标 -> 标准设备坐标 -> 屏幕空间坐标

  原本世界坐标转换到观察空间坐标需要乘上视图矩阵 CameraMatrixWorldInverse(ViewMatrix)

  随后,观察空间坐标转换到裁剪空间坐标需要乘上相机投影矩阵:ProjectMatrix

  在 ThreeJS 中有一个方法 Vector3.project(camera) 综合了这两步:

project( camera ) {return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );
}

屏幕坐标系和标准设备坐标

  ThreeJS 是使用了 canvas 画布绘制图形的,因此屏幕坐标系就是 canvas 中的坐标系,也就是左上角是坐标原点:

  在 ThreeJS 中,一个物体可看作一个 MeshMesh 的坐标是用一个 Vector3 来表示的,Vector3 中包含了 xyz 坐标。

  空间坐标系是三维的,其原点默认在屏幕中心,且 x y z 的范围是 [-1,1],因此其 xy 轴在屏幕坐标系中的表示就是:

屏幕坐标转世界坐标

  屏幕坐标转空间坐标需要经过两个步骤:屏幕坐标 -> 标准设备坐标 -> 世界坐标

ThreeJS 中,画布一般是全屏的,因此画布的宽高 w,h 就是:window.innerWidthwindow.innerHeight,所以 Three 的空间坐标系中点 (cx, cy)在屏幕坐标系中就是:(w / 2,h / 2)

(根据情况,有时候宽高会是 canvas.offsetWidthcanvas.offsetHeight

假设 canvas 中有一点 (x,y),这个点在空间坐标系中为 (x1,y1),那么这个转换公式是:
x1=(x/w)∗2−1x1 = (x / w) * 2 - 1 x1=(x/w)∗2−1

y1=−(y/h)∗2+1y1 = -(y / h) * 2 + 1 y1=−(y/h)∗2+1

公式推导过程如下

  1. 首先,我们知道了空间坐标系中点在屏幕坐标系中的表示:cx=w/2cx = w / 2cx=w/2,cy=h/2cy = h / 2cy=h/2

  2. 那么,屏幕坐标系中的点 (x,y)(x, y)(x,y) 应用这个原点 (cx,cy)(cx, cy)(cx,cy) 后的表示为:x′=x−cxx' = x - cxx′=x−cx,y′=cy−yy' = cy - yy′=cy−y (因为这两个坐标系的 y 轴方向是相反的)

  3. 然后再将 (x′,y′)(x', y')(x′,y′) 标准化到 [−1,1][-1, 1][−1,1] 之间,也就是分别除以 cxcxcx 、cycycy :
    x′cx=x−cxcx=xw2−1=(x/w)∗2−1\frac{x'}{cx} = \frac{x - cx}{cx} = \frac{x}{\frac{w}{2}} - 1 = (x / w) * 2 - 1 cxx′​=cxx−cx​=2w​x​−1=(x/w)∗2−1
    同理:
    y′cy=cy−ycy=1+−yw2=−(y/h)∗2+1\frac{y'}{cy} = \frac{cy - y}{cy} = 1 + \frac{-y}{\frac{w}{2}} = -(y / h) * 2 + 1 cyy′​=cycy−y​=1+2w​−y​=−(y/h)∗2+1

使用代码表示就是

const x = event.clientX;//鼠标单击坐标X
const y = event.clientY;//鼠标单击坐标Y// 屏幕坐标转标准设备坐标
const x1 = ( x / window.innerWidth ) * 2 - 1;
const y1 = -( y / window.innerHeight ) * 2 + 1;
//标准设备坐标(z=0.5这个值并没有一个具体的说法)
const stdVector = new Vector3(x1, y1, 0.5);

然后,再通过 Vector3.unproject(camera) 方法将标准设备坐标转为世界坐标:

const worldVector = stdVector.unproject(camera);

世界坐标转屏幕坐标

与上面的坐标转换相反,世界坐标转屏幕坐标的过程为:世界> 标准设备坐标 -> 屏幕坐标

  通过 Vector3对象的方法 project(camera),返回的结果是世界坐标 worldVectorcamera相机对象矩阵变化下对应的标准设备坐标, 标准设备坐标 xyz 的范围是[-1,1]

同样的,假设画布宽为 w ,高为 h,屏幕坐标系中的一点为 (x, y),标准设备坐标系中对应的点为 (x1, y1)

  从标准设备坐标系转换到屏幕坐标系与我们前面计算出的公式相反:
x=x1∗w2+w2x = x1 * \frac{w}{2} + \frac{w}{2} x=x1∗2w​+2w​

y=y1∗h2−h2y = y1 * \frac{h}{2} - \frac{h}{2} y=y1∗2h​−2h​

  首先计算出屏幕坐标系中心

const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;

  计算出的 centerXcenterY 同时也表示了坐标轴的一半大小。

  然后,将设备坐标系使用 project 方法转换到标准设备坐标系,再转换到屏幕坐标系中:

const standardVec = worldVector.project(camera);const screenX = Math.round(centerX * standardVec.x + centerX);
const screenY = Math.round(-centerY * standardVec.y + centerY);

ThreeJS 屏幕坐标与世界坐标互转相关推荐

  1. U3D屏幕坐标,世界坐标,像素坐标之间的关系

    U3D屏幕坐标,世界坐标,像素坐标之间的关系 U3D中,屏幕坐标和世界坐标单位一样,二者之间是直接的一一对应关系,不受屏幕分辨率影响.默认情况下屏幕空间画布的左下角坐标是世界原点(0,0,0),这种情 ...

  2. OSG——- 对点选物体平移(鼠标点选物体、物体随鼠标移动、屏幕坐标转世界坐标)

    之前的一篇博文是有一篇对点选物体进行平移.缩放旋转.那一篇是很简单的调用了OSG中定义的一些dragger,但这些dragger都有坐标轴或者tapbox等在模型上,与我最近要做的事情的需求不同.网上 ...

  3. UE5如何将屏幕坐标转为世界坐标和世界方向

    哈喽,大家好,一起分享一下关于UE5如何将屏幕坐标转为世界坐标的方法. 屏幕坐标转为世界坐标 在PlayerControoler下找到这个函数: DeprojectScreenPositionToWo ...

  4. Unity中屏幕坐标与世界坐标之间的转换

    世界坐标系转换为屏幕坐标 屏幕坐标转世界坐标 using System.Collections; using System.Collections.Generic; using UnityEngine ...

  5. d3.js 获取当前像素坐标_Cesium开发入门篇 | 06坐标系及坐标变换

    Cesium中常用的坐标 1.屏幕坐标(像素) 即二维笛卡尔平面坐标,我们通过鼠标点击直接获取的坐标就是屏幕坐标了,单位是像素值,也可以通过new Cesium.Cartesian2(x, y)创建. ...

  6. cesium camera相机系统

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 概括 一.cesium常见的坐标系 二.几种坐标系的转换方法 1.经纬度坐标转世界坐标 2.世界坐标转经纬度 3.弧度和经纬 ...

  7. 【Unity3D】世界坐标与屏幕坐标

    Unity3D由于是在三维世界中编程,而最终的结果是需要反馈到肉眼所示的2D屏幕之上的.这就产生了一种比较需要考虑的问题,尤其在一些涉及屏幕与Unity3D的3D世界交互的情况.网络上对于这方面的文字 ...

  8. Cocos Creator 世界坐标转屏幕坐标

    Cocos creator某一坐标转屏幕坐标(screen position),以前都是屏幕坐标转世界坐标. 先上代码为敬(只是提供一种思路,有更好的实现和建议欢迎留言) //Scene的设计分辨率是 ...

  9. threejs旋转模型动画教程

    第一次使用threejs到实际项目中,开始的时候心情有点小激动,毕竟是第一次嘛,然而做着做着就感受到这玩意水好深,满满的都是坑,填都填不过来.经过老板20天惨无人道的摧残,终于小有成就. 因为第一次搞 ...

最新文章

  1. Alpha 冲刺报告(3/10)
  2. 你知道这些产品设计灵感网站吗?
  3. ACM的输入输出总结
  4. 关闭加速渲染_“瀑布屏”旗舰 摩托罗拉Edge+渲染图曝光,Moto G8正式发布
  5. 为什么谈及硬件,必言软件?软硬件协同让开源世界“阴阳调和”
  6. 白宫发布太空系统网络安全防护指令
  7. Codeforces 486D Valid Sets (树型DP)
  8. NutUI 2.1.0 发布,移动端轻量级 Vue 组件库
  9. vi/vim命令使用
  10. SqlServer日常积累(二)
  11. Java TemplateProcessingException之Cannot execute subtraction: operands are null and #1234
  12. 程序员的快乐到底是什么?
  13. 高中OJ3837. 【NOIP2014模拟9.14】心灵终结
  14. java 帕斯卡,帕斯卡三角 有多少种可能,java编程 帕斯卡三角形 立刻求高手!! 给满分...
  15. java语言(3):通俗易懂的泛型
  16. 2020软件下载站源码手机自适应
  17. 安卓使用无障碍服务监听微信和QQ的收款信息
  18. SAP 过账显示不在允许过账期间 消息号M7053
  19. ARP地址解析协议详解
  20. 哪个男孩不想完成一次快速的查询?从MySQL、ES、HBASE等技术一起探讨下!

热门文章

  1. sicily 1004 I Conduit!
  2. windows定时关机
  3. 精通Zookeeper系列开篇:进大厂不得不学的分布式协同利器!
  4. 一文读懂Spring中的AOP机制
  5. 1000+个常用的Linux命令!看完通关!随手玩Linux!
  6. 突破高连接性能瓶颈,图数据库在银行业这么用
  7. 如何快速融入团队(三)
  8. 中小型金融企业该如何进行灾备建设?
  9. 分布式之elk日志架构的演进
  10. 干货!推荐8个强大好用的办公软件,解决很多需求