使用 Projection 在 Three.js 中将世界坐标转换为屏幕坐标

Posted

技术标签:

【中文标题】使用 Projection 在 Three.js 中将世界坐标转换为屏幕坐标【英文标题】:Converting World coordinates to Screen coordinates in Three.js using Projection 【发布时间】:2012-07-20 03:41:36 【问题描述】:

在 Three.js 中有几个关于 unprojecting 的优秀堆栈问题 (1, 2),即如何将浏览器中的 (x,y) 鼠标坐标转换为 (x,y,z) 坐标在 Three.js 画布空间中。他们大多遵循这种模式:

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

我一直试图做相反的事情——而不是从“屏幕到世界”空间,而是从“世界到屏幕”空间。如果我知道 Three.js 中对象的位置,如何确定它在屏幕上的位置?

似乎没有针对此问题的任何已发布解决方案。 Another question about this just showed up on Stack,但作者声称已经解决了一个对我不起作用的功能的问题。他们的解决方案不使用投影射线,我很确定由于 2D 到 3D 使用 unprojectVector(),因此 3D 到 2D 解决方案将需要 projectVector()。

还有this issue在Github上打开。

感谢任何帮助。

【问题讨论】:

非常感谢“屏幕到世界”的翻译模式。痛了好几个小时.. 【参考方案1】:

试试这个:

var width = 640, height = 480;
var widthHalf = width / 2, heightHalf = height / 2;

var vector = new THREE.Vector3();
var projector = new THREE.Projector();
projector.projectVector( vector.setFromMatrixPosition( object.matrixWorld ), camera );

vector.x = ( vector.x * widthHalf ) + widthHalf;
vector.y = - ( vector.y * heightHalf ) + heightHalf;

【讨论】:

另一个堆栈问题的操作发布了这个链接,它也有解决方案。再次感谢! github.com/mrdoob/three.js/issues/78 getPosition() 已被弃用。改用这个: var vector = projection.projectVector(new THREE.Vector3().getPositionFromMatrix(object.matrixWorld), camera); 更新了答案。谢谢@imbrizi! 我认为您的代码在 getPositionFromMatrix 之前缺少向量。所以vector.getPositionFromMatrix 再次修复。对此感到抱歉。【参考方案2】:

对于现代 Three.js (r75),可以将矢量投影到屏幕上:

var width = window.innerWidth, height = window.innerHeight;
var widthHalf = width / 2, heightHalf = height / 2;

var pos = object.position.clone();
pos.project(camera);
pos.x = ( pos.x * widthHalf ) + widthHalf;
pos.y = - ( pos.y * heightHalf ) + heightHalf;

【讨论】:

请注意,如果对象是组的一部分,object.position 可能不是您需要的,因为object.position 是组中的本地位置。使用object.getWorldPosition()(或pos.setFromMatrixPosition(),如其他答案)将考虑组内的相对定位。 此答案假定画布与窗口大小相同。真的应该使用一半画布宽度和一半画布高度...... 如果您想准确地获取位置,而不管包含画布的位置和大小(可能不是整个窗口),您需要将这些计算基于canvas.getBoundingClientRect() window【参考方案3】:

对于获取deprecatedwarnings 日志的每个人,可接受的答案是针对较旧的 Three.js 版本。现在更容易了:

let pos = new THREE.Vector3();
pos = pos.setFromMatrixPosition(object.matrixWorld);
pos.project(camera);

let widthHalf = canvasWidth / 2;
let heightHalf = canvasHeight / 2;

pos.x = (pos.x * widthHalf) + widthHalf;
pos.y = - (pos.y * heightHalf) + heightHalf;
pos.z = 0;

console.log(pos);

【讨论】:

“对象”未定义 这很好,但是当对象在相机后面时,pos.project() 返回一个值,好像对象围绕相机旋转了 180 度。 @Jespertheend 你知道这个问题的任何解决方案吗? @Jespertheend 感谢您的回答。我通过创建从相机到对象的 directionVector 并将其与相机中的 lookAt-Vector 进行比较来解决它。你可以在这里看到它的实际效果(browser-mmo):g13.ennix.io

以上是关于使用 Projection 在 Three.js 中将世界坐标转换为屏幕坐标的主要内容,如果未能解决你的问题,请参考以下文章

使用Three.js 心得系列一 如何创建三维网格三使用Three.js 心得系列三 Three.js 如何加载GlTF文件

如何让 three.js 在 React Native 中运行(没有 WebView)?

使用three.js设置透明背景

在three.js中使用正交相机进行不精确的光线投射

在 three.js 中的球体上使用纹理

Three.js Collada - 在三个 js 中加载多个 Collada 对象