鼠标/canvasX,Y到Three.js世界X,Y,Z
我search了一个与我的用例相匹配的例子,但找不到一个例子。 我试图将屏幕鼠标坐标转换为3D世界坐标考虑到相机。
我find的解决scheme都是通过射线交叉来实现对象拾取。
我想要做的是将Three.js对象的中心定位在鼠标当前“结束”的坐标上。
我的相机是在x:0,y:0,z:500(虽然它会在模拟过程中移动),而我所有的物体都在z = 0,具有不同的x和y值,所以我需要知道世界X,Y假定az = 0的对象将遵循鼠标的位置。
这个问题看起来像一个类似的问题,但没有解决scheme: 在THREE.js中获取鼠标的坐标相对于三维空间
给定鼠标在“左上angular= 0,0 |右下= window.innerWidth,window.innerHeight”的范围上的位置,任何人都可以提供一个解决scheme,将Three.js对象移动到鼠标坐标沿z = 0?
你不需要在场景中有任何物体来做到这一点。
你已经知道相机的位置。
使用vector.unproject( camera )
你可以得到一个光线指向你想要的方向。
您只需要从相机位置延伸该光线,直到光线尖端的z坐标为零。
你可以这样做:
var vector = new THREE.Vector3(); vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 ); vector.unproject( camera ); var dir = vector.sub( camera.position ).normalize(); var distance = - camera.position.z / dir.z; var pos = camera.position.clone().add( dir.multiplyScalar( distance ) );
variablespos
是三维空间中点的位置,“鼠标下”和平面z=0
。
编辑:更新为three.js r.69
在r.58中,这段代码适用于我:
var planeZ = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); var mv = new THREE.Vector3( (event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5 ); var raycaster = projector.pickingRay(mv, camera); var pos = raycaster.ray.intersectPlane(planeZ); console.log("x: " + pos.x + ", y: " + pos.y);
获取3d对象的鼠标坐标use projectVector:
var width = 640, height = 480; var widthHalf = width / 2, heightHalf = height / 2; var projector = new THREE.Projector(); var vector = projector.projectVector( object.matrixWorld.getPosition().clone(), camera ); vector.x = ( vector.x * widthHalf ) + widthHalf; vector.y = - ( vector.y * heightHalf ) + heightHalf;
要获取与特定鼠标坐标相关的three.js 3D坐标,请使用相反的unprojectVector:
var elem = renderer.domElement, boundingRect = elem.getBoundingClientRect(), x = (event.clientX - boundingRect.left) * (elem.width / boundingRect.width), y = (event.clientY - boundingRect.top) * (elem.height / boundingRect.height); var vector = new THREE.Vector3( ( x / WIDTH ) * 2 - 1, - ( y / HEIGHT ) * 2 + 1, 0.5 ); projector.unprojectVector( vector, camera ); var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() ); var intersects = ray.intersectObjects( scene.children );
这里有一个很好的例子。 但是,要使用项目向量,必须有用户点击的对象。 相交将是鼠标位置上的所有对象的数组,而不pipe它们的深度如何。
(Un)ProjectVector和projector.pickingRay()的解决scheme不再工作,只是完成了更新我自己的代码..所以最近的工作版本应该是如下:
var rayVector = new THREE.Vector3(0, 0, 0.5); var camera = new THREE.PerspectiveCamera(fov,this.offsetWidth/this.offsetHeight,0.1,farFrustum); var raycaster = new THREE.Raycaster(); var scene = new THREE.Scene(); //... function intersectObjects(x, y, planeOnly) { rayVector.set(((x/this.offsetWidth)*2-1), (1-(y/this.offsetHeight)*2), 1).unproject(camera); raycaster.set(camera.position, rayVector.sub(camera.position ).normalize()); var intersects = raycaster.intersectObjects(scene.children); return intersects; }
下面是我根据WestLangley的回复写的一个ES6课,在THREE.JS R77里完全适合我。
请注意,它假定您的渲染视口将占用您的整个浏览器视口。
class CProjectMousePosToXYPlaneHelper { constructor() { this.m_vPos = new THREE.Vector3(); this.m_vDir = new THREE.Vector3(); } Compute( nMouseX, nMouseY, Camera, vOutPos ) { let vPos = this.m_vPos; let vDir = this.m_vDir; vPos.set( -1.0 + 2.0 * nMouseX / window.innerWidth, -1.0 + 2.0 * nMouseY / window.innerHeight, 0.5 ).unproject( Camera ); // Calculate a unit vector from the camera to the projected position vDir.copy( vPos ).sub( Camera.position ).normalize(); // Project onto z=0 let flDistance = -Camera.position.z / vDir.z; vOutPos.copy( Camera.position ).add( vDir.multiplyScalar( flDistance ) ); } }
你可以像这样使用这个类:
// Instantiate the helper and output pos once. let Helper = new CProjectMousePosToXYPlaneHelper(); let vProjectedMousePos = new THREE.Vector3(); ... // In your event handler/tick function, do the projection. Helper.Compute( e.clientX, e.clientY, Camera, vProjectedMousePos );
vProjectedMousePos现在包含z = 0平面上的投影鼠标位置。
这是我创build一个es6课程。 与Three.js r83合作。 使用rayCaster的方法来自mrdoob这里: Three.js Projector和Ray对象
export default class RaycasterHelper { constructor (camera, scene) { this.camera = camera this.scene = scene this.rayCaster = new THREE.Raycaster() this.tapPos3D = new THREE.Vector3() this.getIntersectsFromTap = this.getIntersectsFromTap.bind(this) } // objects arg below needs to be an array of Three objects in the scene getIntersectsFromTap (tapX, tapY, objects) { this.tapPos3D.set((tapX / window.innerWidth) * 2 - 1, -(tapY / window.innerHeight) * 2 + 1, 0.5) // z = 0.5 important! this.tapPos3D.unproject(this.camera) this.rayCaster.set(this.camera.position, this.tapPos3D.sub(this.camera.position).normalize()) return this.rayCaster.intersectObjects(objects, false) } }
如果您想检查场景中的所有对象以便点击,则可以像这样使用它。 我做了上面的recursion标志,因为我的用途我不需要它。
var helper = new RaycasterHelper(camera, scene) var intersects = helper.getIntersectsFromTap(tapX, tapY, this.scene.children) ...