ThreeJS中将屏幕空间XY坐标转换为指定Z位置透视投影坐标的方法
透视相机下固定屏幕锚点+指定深度物体实现方案
你的思路方向是对的,核心就是通过矩阵逆运算将屏幕坐标映射到指定深度的3D世界坐标,不需要使用正交相机,物体全程处于透视投影空间,可随时脱离锚定状态参与物理交互,具体实现如下:
核心原理
透视投影的本质是把3D世界坐标通过视图矩阵(相机空间转换)、投影矩阵(裁剪空间转换)映射到[-1,1]范围的归一化设备坐标(NDC),反过来把指定屏幕位置的NDC坐标配合目标深度,通过两个矩阵的逆运算就能还原回世界坐标。ThreeJS已经封装好了现成的运算方法,不需要手动推导矩阵。
- 坐标归一化转换
屏幕坐标原点默认在左上角,先转成WebGL标准NDC坐标(x、y范围均为[-1,1],x=-1对应屏幕最左、x=1对应最右,y=-1对应屏幕最下、y=1对应最上):// 以左下角锚点为例,screenX=0、screenY=窗口高度 const ndcX = (screenX / window.innerWidth) * 2 - 1 const ndcY = -(screenY / window.innerHeight) * 2 + 1 - 目标位置计算
你需要物体距离相机5个单位长度,直接通过反投影拿到屏幕点对应的世界空间射线,沿射线取对应长度的点即可,不需要手动计算逆矩阵:// 全局复用向量,避免每帧创建对象产生性能开销 const rayPoint = new THREE.Vector3() const targetPos = new THREE.Vector3() /** * 更新锚定物体位置 * @param {THREE.PerspectiveCamera} camera 场景透视相机 * @param {THREE.Object3D} anchorObj 需要锚定的3D物体 * @param {number} screenX 锚点屏幕x坐标 * @param {number} screenY 锚点屏幕y坐标 * @param {number} distanceToCamera 物体到相机的欧氏距离 */ function updateAnchor(camera, anchorObj, screenX, screenY, distanceToCamera) { // 填充NDC坐标,z值填0.5仅用于获取射线方向 rayPoint.set( (screenX / window.innerWidth) * 2 - 1, -(screenY / window.innerHeight) * 2 + 1, 0.5 ) // 反投影得到射线上的世界空间点 rayPoint.unproject(camera) // 计算相机到该点的单位方向向量 const dir = rayPoint.sub(camera.position).normalize() // 沿方向移动指定距离得到目标位置 targetPos.copy(camera.position).addScaledVector(dir, distanceToCamera) // 更新物体位置 anchorObj.position.copy(targetPos) } - 调用时机
把updateAnchor方法放到渲染循环中,在相机移动、旋转、视口尺寸变化、相机fov调整后执行即可。锚定状态下物体的屏幕相对位置、透视视觉尺寸会完全保持固定,和你预期的效果一致。
注意:该方案下锚定物体是普通的透视空间3D对象,没有任何特殊层级或渲染约束,需要取消锚定时直接停止调用
updateAnchor即可,物体会停在当前位置,可直接接入物理系统和其他场景对象交互,无需额外转换。
内容的提问来源于stack exchange,提问作者Kyle
相关产品推荐
相关产品推荐

