Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
鼠标/画布X,Y到Three.js World X,Y,Z_Three.js_Point_Projection_Mouse Coordinates - Fatal编程技术网

鼠标/画布X,Y到Three.js World X,Y,Z

鼠标/画布X,Y到Three.js World X,Y,Z,three.js,point,projection,mouse-coordinates,Three.js,Point,Projection,Mouse Coordinates,我已经到处搜索了一个与我的用例相匹配的示例,但没有找到。考虑到摄像机,我试图将屏幕鼠标坐标转换为3D世界坐标 我找到的解决方案都是通过光线相交来实现对象拾取 我要做的是将Three.js对象的中心定位在鼠标当前“结束”的坐标上 我的相机位于x:0,y:0,z:500(尽管它在模拟过程中会移动),我的所有对象都位于z=0,x和y值不同,因此我需要知道世界x,y,前提是假设将跟随鼠标位置的对象的z=0 此问题看起来类似于一个问题,但没有解决方案: 如果鼠标在屏幕上的位置范围为“左上=0,0 |右下=

我已经到处搜索了一个与我的用例相匹配的示例,但没有找到。考虑到摄像机,我试图将屏幕鼠标坐标转换为3D世界坐标

我找到的解决方案都是通过光线相交来实现对象拾取

我要做的是将Three.js对象的中心定位在鼠标当前“结束”的坐标上

我的相机位于x:0,y:0,z:500(尽管它在模拟过程中会移动),我的所有对象都位于z=0,x和y值不同,因此我需要知道世界x,y,前提是假设将跟随鼠标位置的对象的z=0

此问题看起来类似于一个问题,但没有解决方案:


如果鼠标在屏幕上的位置范围为“左上=0,0 |右下=window.innerWidth,window.innerHeight”,有人能提供一种解决方案,将Three.js对象沿z=0移动到鼠标坐标上吗?

使用projectVector获取3d对象的鼠标坐标:

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;
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 );
要获取与特定鼠标坐标相关的三个.js 3D坐标,请使用相反的未投影向量:

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;
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 );

有一个很好的例子。但是,要使用project vector,必须有一个用户单击的对象。相交将是鼠标位置上所有对象的数组,而不管其深度如何。

执行此操作时,不需要场景中有任何对象

你已经知道摄像机的位置了

使用
vector.unproject(摄影机)
可以获得指向所需方向的光线

您只需要从摄影机位置延伸该光线,直到光线尖端的z坐标为零

您可以这样做:

var vec = new THREE.Vector3(); // create once and reuse
var pos = new THREE.Vector3(); // create once and reuse

vec.set(
    ( event.clientX / window.innerWidth ) * 2 - 1,
    - ( event.clientY / window.innerHeight ) * 2 + 1,
    0.5 );

vec.unproject( camera );

vec.sub( camera.position ).normalize();

var distance = - camera.position.z / vec.z;

pos.copy( camera.position ).add( vec.multiplyScalar( distance ) );
变量
pos
是点在三维空间“鼠标下”和平面
z=0
中的位置


编辑:如果需要“鼠标下”平面中的点
z=targetZ
,请将距离计算替换为:

var distance = ( targetZ - camera.position.z ) / vec.z;

three.js r.98

在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);

ThreeJS正在慢慢地从ProjectVector中删除。(Un)ProjectVector和Projector.pickingRay()的解决方案不再起作用,只是完成了我自己的代码更新。。因此,最新的工作版本应如下所示:

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平面上的投影鼠标位置。

以下是我在创建es6类时的感受。使用Three.js r83。使用rayCaster的方法来源于mrdoob:

如果要对照场景中的所有对象检查命中率,可以这样使用它。我将上面的递归标志设为false,因为对于我的使用,我不需要它

var helper = new RaycasterHelper(camera, scene)
var intersects = helper.getIntersectsFromTap(tapX, tapY, 
this.scene.children)
...

这对我来说在使用
正交摄影机时很有效

let vector = new THREE.Vector3();
vector.set(
    (event.clientX / window.innerWidth) * 2 - 1,
    - (event.clientY / window.innerHeight) * 2 + 1,
    0
);
vector.unproject(camera);

WebGL three.js r.89

尽管提供的答案在某些场景中可能有用,但我几乎无法想象这些场景(可能是游戏或动画),因为它们根本不精确(猜测目标的NDC z?)。如果知道目标z平面,则不能使用这些方法将屏幕坐标取消投影到世界坐标。但在大多数情况下,你应该知道这架飞机

例如,如果您按圆心(模型空间中的已知点)和半径绘制球体-您需要将半径作为未投影鼠标坐标的增量-但您不能!恕我直言@WestLangley的targetZ方法不起作用,它给出了错误的结果(如果需要,我可以提供jsfiddle)。另一个例子-您需要通过鼠标双击来设置“动态观察控制”目标,但不使用场景对象的“真实”光线投射(当您没有要拾取的对象时)

对我来说,解决方案是在目标点沿z轴创建虚拟平面,然后在此平面上使用光线投射。目标点可以是当前的动态观察控制目标或对象的顶点,您需要在现有模型空间中一步一步地绘制。这非常有效,而且非常简单(例如typescript中的示例):


我的画布比我的整个窗口小,需要确定点击的世界坐标:

// get the position of a canvas event in world coords
function getWorldCoords(e) {
  // get x,y coords into canvas where click occurred
  var rect = canvas.getBoundingClientRect(),
      x = e.clientX - rect.left,
      y = e.clientY - rect.top;
  // convert x,y to clip space; coords from top left, clockwise:
  // (-1,1), (1,1), (-1,-1), (1, -1)
  var mouse = new THREE.Vector3();
  mouse.x = ( (x / canvas.clientWidth ) * 2) - 1;
  mouse.y = (-(y / canvas.clientHeight) * 2) + 1;
  mouse.z = 0.5; // set to z position of mesh objects
  // reverse projection from 3D to screen
  mouse.unproject(camera);
  // convert from point to a direction
  mouse.sub(camera.position).normalize();
  // scale the projected ray
  var distance = -camera.position.z / mouse.z,
      scaled = mouse.multiplyScalar(distance),
      coords = camera.position.clone().add(scaled);
  return coords;
}

var canvas = renderer.domElement;
canvas.addEventListener('click', getWorldCoords);
这里有一个例子。单击滑动前后甜甜圈的相同区域,您会发现坐标保持不变(检查浏览器控制台):

//three.js样板文件
var container=document.querySelector('body'),
w=container.clientWidth,
h=容器的重量,
场景=新的三个。场景(),
摄像机=新的三视角摄像机(75,宽/高,0.001100),
控件=新的三个.map控件(摄影机、容器),
renderConfig={antialas:true,alpha:true},
renderer=new THREE.WebGLRenderer(renderConfig);
控制点速度=0.4;
摄像机位置设置(0,0,-10);
renderer.setPixelRatio(window.devicePixelRatio);
渲染器。设置大小(w,h);
container.appendChild(renderer.domeElement);
addEventListener('resize',function()){
w=container.clientWidth;
h=容器的重量;
摄像头。纵横比=w/h;
camera.updateProjectMatrix();
渲染器。设置大小(w,h);
})
函数render(){
请求动画帧(渲染);
渲染器。渲染(场景、摄影机);
控件更新();
}
//画一些几何图形
var geometry=新的三个圆环几何体(10,3,16,100,);
var material=new THREE.MeshNormalMaterial({color:0xFFFFFF00,});
var torus=新的三个网格(几何体、材质);
场景。添加(圆环体);
//将单击坐标转换为世界空间
//获取画布事件在世界坐标中的位置
函数getWorldCoords(e){
//将x,y坐标放入发生单击的画布中
var rect=canvas.getBoundingClientRect(),
x=e.clientX-rect.left,
y=e.clientY-rect.top;
//将x、y转换为剪辑空间;坐标从左上角顺时针: