前言
开发中遇到需求需要点击屏幕位置处,生成一个类似圆形弹窗面板,这个交互需要进行的坐标转换为模型坐标(局部坐标)=>场景坐标(世界坐标)=>标准设备坐标=>屏幕空间坐标,也就是一个将3D坐标转换为2D坐标的过程,话不多说,上代码!
模型坐标转场景坐标
模型坐标是以模型为原点绘制xyz轴的坐标点,先将模型坐标点通过与模型的matrix相乘就能得到场景中的坐标
// 假设point是模型中的vector3坐标点
const point = new THREE.Vector3(10, 10, 10)
// point2为转换出的场景中的坐标,mesh为此模型的实体
const point2 = point.clone().applyMatrix4(mesh.matrix)
场景坐标转设备坐标
场景坐标是以vector3(0,0,0)为原点的坐标点,通过vector3的project(camera)投射的相机中,返回在camera相机对象矩阵变化下对应的标准设备坐标,标准设备坐标的xyz范围是[-1, 1]
// point3是设备坐标
const point3 = point2.clone().project(scene.camera)
设备坐标转屏幕坐标
这里先写转换公式,后面会展示推导过程
// point4就是转换的屏幕坐标
const halfWidth = scene.dom.offsetWidth / 2
const halfHeight = scene.dom.offsetHeight / 2
const point4 = new THREE.Vector3(0, 0, 0)
point4.x = Math.round(point3.x * halfWidth + halfWidth)
point4.y = Math.round(point3.y * halfHeight + halfHeight)
设备坐标转屏幕的公式推导
threejs是通过canvas画布绘制图形的,因为屏幕坐标系就是canvas中的坐标系,也就是左上角[0, 0]是坐标原点,在threejs中一个物体的坐标是三维坐标,原点默认在屏幕中心[0, 0, 0],且xyz的范围是[-1, 1],因此图片表示为
通过Vector3对象的方法project,方法的参数是相机对象,语句worldVector.project(camera);返回的结果是世界坐标worldVector在camera相机对象矩阵变化下对应的标准设备坐标, 标准设备坐标xyz的范围是[-1,1]。
ThreeJS 中,画布一般是全屏的,因此画布的宽高 w,h 就是:window.innerWidth 和 window.innerHeight,所以 Three 的空间坐标系中点 (cx, cy)在屏幕坐标系中就是:(w / 2,h / 2)。
假设 canvas 中有一点 (x,y),这个点在空间坐标系中为 (x1,y1),那么这个转换公式是:
x1=(x/w)∗2−1
y1=−(y/h)∗2+1
3D和2D之间就可以通过固定公式进行转换
转换出来坐标的坑
这个时候转换出来的坐标x,y,是相对于canvas的,换到页面中,如果还有边距这些属性的话也应该加上那段距离这个点才是跟鼠标点击的点完美切合