vlambda博客
学习文章列表

ThreeJS零基础入门-三种坐标系位置数据的转换

使用ThreeJS进行交互和动画是经常需要用到坐标转换,比如从屏幕坐标转换为世界坐标,或者进行反向转换。两者之间的相互转换主要是以下三种坐标系之间的转换:屏幕坐标、标准坐标、世界坐标系。

屏幕坐标系和标准坐标系

先来了解一下这两个坐标系的定义,具体如下图所示:

可以看到屏幕上坐标系的起点(0,0)在左上角,而标准坐标系的起点在canvas中心处。

屏幕坐标转换为3D世界坐标

假设3D画布的大小填满window

假如我们要通过鼠标来操控3D画布内的场景对象,需要将鼠标的坐标位置转换为3D世界坐标,具体流程为屏幕坐标->标准坐标->世界坐标

具体代码示例如下:

   import {Vector3} from 'three';

  onMouseClicked() {
       const mouseX = event.clientX;//鼠标单击坐标X
       const mouseY = event.clientY;//鼠标单击坐标Y

       // 屏幕坐标转标准设备坐标
       const x = ( mouseX / window.innerWidth ) * 2 - 1;
       const y = -( mouseY / window.innerHeight ) * 2 + 1;
       //标准设备坐标(z=0.5这个值并没有一个具体的说法)
       const stdVector = new Vector3(x, y, 0.5);

       // 通过unproject方法,可以将标准设备坐标转世界坐标
       const worldVector = stdVector.unproject(camera);
       // 进行剩下操作,比如判断鼠标是否选中某个物体
  }
   // 窗口鼠标单击
   // window.addEventListener('click',onMouseClicked);

假设3D画布的只是window中的一个窗口,比如某个div内的canvas

这时候只要获取到canvas所在的div的宽高及左上位移数据就可以计算了,具体如下:

    const mouseX = event.clientX;//鼠标单击坐标X
   const mouseY = event.clientY;//鼠标单击坐标Y
   const rect = someDiv.getBoundingClientRect();
   const x = ((mouseX - rect.left) / someDiv.clientWidth) * 2 - 1;
   const y = - ((mouseY - rect.top) / someDiv.clientHeight) * 2 + 1;
   const stdVector = new Vector3(x, y, 0.5);
   // 通过unproject方法,可以将标准设备坐标转世界坐标
   const worldVector = stdVector.unproject(camera);


3D世界坐标转换为屏幕坐标

反过来,如果要求出物体响应的屏幕坐标,世界坐标->标准坐标->屏幕坐标,那么可以这样:

// const box = ...
const worldVector = new Vector3(
   box.position.x,
   box.position.y,
   box.position.z
);
//世界坐标转标准设备坐标
const stdVector = worldVector.project(camera);
const a = window.innerWidth / 2;
const b = window.innerHeight / 2;
//标准设备坐标转屏幕坐标x,y
const x = Math.round(stdVector.x * a + a);
const y = Math.round(-stdVector.y * b + b);

这时候只要获取到canvas所在的div的宽高及左上位移数据就可以计算了,具体如下:

// const box = ...
const worldVector = new Vector3(
   box.position.x,
   box.position.y,
   box.position.z
);
const rect = someDiv.getBoundingClientRect();
//世界坐标转标准设备坐标
const stdVector = worldVector.project(camera);
const a = rect.width / 2;
const b = rect.height / 2;
//标准设备坐标转屏幕坐标x,y
const x = Math.round(stdVector.x * a + a) + rect.left;
const y = Math.round(-stdVector.y * b + b) + rect.top;