在三维空间中项目模拟一个立体的球在屏幕中从左移动到右边,然后在从屏幕的左边出来,继续重复移动,类似与二维动画的跑马灯,那么问题就来了,如何将球定位到屏幕的最左边呢?最初我使用网上搜索的计算视锥的高和宽来控制,具体代码如下:
通过视锥高宽计算
let frustumHeight = 2.0 * distance * Math.tan(camera.fov * 0.5 * THREE.Math.DEG2RAD);
let frustumWidth = frustumHeight * camera.aspect;
let tempV = item.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix);
if(tempV.x>1){
console.log('右边越界');
//强制移动到最左边
movingCube.position.x = -frustumWidth - movingCube.userData.radius;
}
if(tempV.x<-1){
console.log('左边越界')
//todo
}
if(tempV.y>1){
console.log('上边越界')
//todo
}
if(tempV.y<-1){
console.log('下边边越界')
//todo
}
发现使用这种方面,只有当相机位置在【0,0,x】,x>0的位置上,计算的结果才是正确的,所以放弃这种算法
于是考虑既然整个投影是在视锥体中进行的,那么我只要计算,物体与视锥体的各个面的距离,然后更改物体的x,y值不就可了吗?
通过视锥体
let frustum = new THREE.Frustum();
frustum.setFromMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse ));
/**
* 根据法线推断
* frustum.planes[0] 右侧的面
* frustum.planes[1] 左侧的面
* frustum.planes[2] 下方的面
* frustum.planes[3] 上方的面
* frustum.planes[4] 远裁剪面
* frustum.planes[5] 近裁剪面
*/
//几点物体与左侧面的距离
let dir = frustum.planes[4].normal
let angle = Math.PI - frustum.planes[1].normal.angleTo(dir);
//点到左侧面的距离
let p2p = frustum.planes[1].distanceToPoint(movingCube.position);
//平行与近裁面的距离
let span = p2p / Math.cos(angle);
sphere.position.y = span * 0.5;
这种方式的结果,与第一中结果一样,还是在X,Y方向上更改相机位置,计算的结果就不对了
[图片上传失败...(image-2cf941-1571057699827)]
最后使用屏幕坐标转化为三维坐标的方式实现,这次很稳定,不管相机和中姿态都能很好的将小球放到屏幕的任何位置,具体实现如下:
射线 与屏幕坐标到三维空间转化
var mouse = new THREE.Vector2();
let raycaster = new THREE.Raycaster();
//相机lookAt的反方向
let dir = camera.getWorldDirection().multiplyScalar(-1);
//定义一个面,同近裁面平行的面,深度值(z)同球的深度中相同
var plane = new THREE.Plane(dir, -10);
renderer.domElement.addEventListener('mousemove', onPointerMove, { passive: false });
function onPointerMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = new THREE.Vector3();
raycaster.ray.intersectPlane(plane, intersects);
// console.log(raycaster.ray);
console.log("pos=====>", mouse, intersects);
//碰撞点就是三维中的位置
sphere.position.copy(intersects);
/***** 测试结果都是正确的 ******************************************************
//最左边
//onPointerMove({ clientX: 0, clientY: window.innerHeight * 0.5 })
//最右边
//onPointerMove({ clientX: window.innerWidth, clientY: window.innerHeight * 0.5 })
//最上边
//onPointerMove({ clientX: window.innerWidth*0.5, clientY: 0 })
//最下边
//onPointerMove({ clientX:window.innerWidth*0.5, clientY: window.innerHeight })
}
前两种方式没有实现,高手支招,🙏
网友评论