ThreeJS:投影与射线投射调整问题求助
解决方案
核心解决思路
别再手动设屏幕点了,直接盯着立方体要对齐的那条白色边缘反向推导:先把这条边缘的两个顶点投影到屏幕,算出这条投影线和屏幕左右边缘的交点,再把这些交点转成世界坐标的红点。这样不管怎么旋转相机,红点连线都会自动和立方体边缘的屏幕投影对齐。
具体实现步骤
- 拿到立方体目标边缘的两个顶点:比如你要对齐的白色边缘对应的两个世界坐标点,记为
cubeEdgeA和cubeEdgeB。 - 把顶点转成屏幕像素坐标:
const screenA = new THREE.Vector3(); const screenB = new THREE.Vector3(); // 先转成ThreeJS标准化设备坐标(NDC) cubeEdgeA.project(camera, screenA); cubeEdgeB.project(camera, screenB); // 再转成浏览器窗口的像素坐标 screenA.x = (screenA.x * 0.5 + 0.5) * window.innerWidth; screenA.y = (-screenA.y * 0.5 + 0.5) * window.innerHeight; screenB.x = (screenB.x * 0.5 + 0.5) * window.innerWidth; screenB.y = (-screenB.y * 0.5 + 0.5) * window.innerHeight; - 计算投影线与屏幕边缘的交点:
用两点式推导直线方程,处理斜率无穷大的边界情况,算出这条线和屏幕左边缘(x=0)、右边缘(x=window.innerWidth)的交点:let leftScreen, rightScreen; // 处理垂直屏幕的边缘(斜率无穷大) if (Math.abs(screenA.x - screenB.x) < 0.001) { leftScreen = new THREE.Vector2(0, screenA.y); rightScreen = new THREE.Vector2(window.innerWidth, screenA.y); } else { const slope = (screenB.y - screenA.y) / (screenB.x - screenA.x); const intercept = screenA.y - slope * screenA.x; leftScreen = new THREE.Vector2(0, intercept); rightScreen = new THREE.Vector2(window.innerWidth, slope * window.innerWidth + intercept); } - 把屏幕交点转成世界坐标红点:
用Raycaster从屏幕交点发射射线,和立方体取交点就是红点的世界坐标:const raycaster = new THREE.Raycaster(); // 更新左红点 raycaster.setFromCamera(leftScreen, camera); const leftHits = raycaster.intersectObject(cube); if (leftHits.length) { leftRedDot.position.copy(leftHits[0].point); } // 更新右红点 raycaster.setFromCamera(rightScreen, camera); const rightHits = raycaster.intersectObject(cube); if (rightHits.length) { rightRedDot.position.copy(rightHits[0].point); } - 绑定到旋转事件:把这段更新逻辑放到
OrbitControls的change回调里,每次旋转相机后自动刷新红点位置:controls.addEventListener('change', () => { // 上面的更新代码放在这里 });
为啥这方法比你想的笨办法好
- 完全自动适配视角旋转,不用手动调点
- 只针对目标边缘计算,性能开销小
- 逻辑全基于ThreeJS原生API,不容易出错,也好维护
注意点
- 如果目标边缘被挡住了,记得加个视锥体检测(用
THREE.Frustum判断顶点是否在相机视野里),避免算出无效的交点 - 处理垂直屏幕的边缘时,要通过极小值判断避免除以0的错误
内容的提问来源于stack exchange,提问作者toowren
相关产品推荐
相关产品推荐

