基于threejs的3d框架 (如何通过three.js实现点云可视化)

webgl三维框架,利用webgl创建3d三棱柱

在Web3D|基于WebGL的Three.js框架|入门篇中我们已经介绍过了,Three.js用的是右手坐标系统,跟我们初中几何中用到的是一样的。

抛开z轴,x和y轴组成的二维平面,也与我们屏幕坐标不一样。通常情况下,不管是Web还是Java,Swift等的UI,坐标都是以屏幕左上角为原点(0,0)的,而与Three.js中的y轴正好相反。

让一个物体显示在屏幕上,两个因素必不可少,一是位置,另一个是大小。在实际的开发中,有时候我们需要将一个Three.js中的位置和大小与屏幕上的位置和大小进行相互转换,那么我们应该怎么办呢?

获取鼠标位置

窗口坐标转成Three坐标。

var mouse = new THREE.Vector2();
function onMouseMove( event ) {
 // calculate mouse position in normalized device coordinates
 // (-1 to +1) for both components
 
 mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
 mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}

位置(position)转换

Three坐标转屏幕坐标

function toScreenPosition(point3d) {
 var threePos = point3d.clone().project(camera);
 var halfWidth = window.innerWidth / 2;
 var halfHeight = window.innerHeight / 2;
 var result = {
 x: threePos.x * halfWidth + halfWidth,
 y: -threePos.y * halfHeight + halfHeight
 }
 return result;
}

屏幕坐标转Three坐标,由于缺少z值,根据需要设定

function toThreePosition(point2d, z) {
 var widthHalf = 0.5 * window.innerWidth;
 var heightHalf = 0.5 * window.innerHeight;
 var zValue = z == undefined ? 0.5 : z;
 var point3d = new THREE.Vector3(
 (point2d.x / widthHalf) * 2 - 1,
 -(point2d.y / heightHalf) * 2 + 1,
 zValue);
 point3d.unproject(camera);
 return point3d;
}

获取位置(position)

// Return the position of object
function getPosition(object) {
 var position = new THREE.Vector3();
 if (object instanceof THREE.Mesh) {
 object.updateMatrixWorld(true);
 position.setFromMatrixPosition(object.matrixWorld);
 }
 return position;
}

获取大小

function getSize(object) {
 if (object instanceof THREE.BufferGeometry) {
 var geometry = new THREE.Geometry().fromBufferGeometry(object);
 return getSize(geometry);
 } else if (object instanceof THREE.Mesh) {
 var box = new THREE.Box3();
 box.setFromObject(object);
 return box.getSize(new THREE.Vector3());
 } else if (object instanceof THREE.Geometry) {
 object.computeBoundingBox();
 return object.boundingBox.getSize(new THREE.Vector3());
 }
 return null;
}

获取中心点

function getCenter(object) {
 if (object instanceof THREE.BufferGeometry) {
 var geometry = new THREE.Geometry().fromBufferGeometry(object);
 return getCenter(geometry);
 } else if (object instanceof THREE.Geometry) {
 object.computeBoundingBox();
 return object.boundingBox.getCenter(new THREE.Vector3());
 } else if (object instanceof THREE.Mesh) {
 var box = new THREE.Box3();
 box.setFromObject(object);
 return box.getCenter(new THREE.Vector3());
 }
 return null;
}

获取Three中的物体在屏幕上的大小

 var positionOfCamera = camera.position;
 var positionOfMesh = mesh.position;
 // Mesh的大小,BBox
 var sizeOfMesh = getSize(mesh);
 // 计算相机到mesh表面的距离,注意:Z轴垂直与mesh表面。
 var dist = positionOfCamera.z - positionOfMesh.z - sizeOfMesh.z / 2;
 // convert vertical fov to radians
 var vFOV = THREE.Math.degToRad(camera.fov);
 // 计算高度
 var height = 2 * Math.tan(vFOV / 2) * dist;
 // 在Three中实际高度与投影高度的一个比值
 var fractionHeight = sizeOfMesh.y / height;
 // 屏幕上的实际高度
 var heightPixels = window.innerHeight * fractionHeight;
 // 计算宽度
 var width = height * camera.aspect;
 var fractionWidth = sizeOfMesh.x / width;
 //屏幕上的宽度
 var widthPixels = window.innerWidth * fractionWidth;
 // Mesh的中心点,BBox
 var center = getCenter(mesh);
 //将3D坐标转换到屏幕坐标
 var centerOnScreen = toScreenPosition(center);

总结

  1. 需要明白,Three中的物体是三维的,可以在空间范围内任意旋转。
  2. 最重要的是,随着物体的旋转,屏幕上显示的只是通过Camera投影过去的那部分,所以大小也会随之变化。