javascript - 在three.js中计算边界框水平边缘的屏幕坐标

标签 javascript html three.js

我有一个呈现一组粒子的 three.js 页面(可运行的演示和代码位于:https://dl.dropboxusercontent.com/u/31495717/cubemaker/index.html)。

这是默认 View 的快照,我们基本上是在查看以 (0, 0, 0) 为中心的单位立方体:

Default view

我有透明的边界框,以这个单位立方体中的每个粒子为中心。如果我修改不透明度,我们就可以看到每个粒子周围的框:

var bounding_box = new THREE.Mesh(
    bounding_box_geometry, 
    new THREE.MeshLambertMaterial({ 
        opacity: 0.5, 
        transparent: true,
        alphaTest: 0.5
    })
);

Default view with bounding boxes

我使用 THREE.Raycaster() 跟踪从相机到当前鼠标位置的世界坐标的矢量:

var mouse_vector = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera);
raycaster.set(camera.position, mouse_vector.sub(camera.position).normalize());
var bounding_box_intersections = raycaster.intersectObjects(bounding_boxes);

如果有任何 bounding_box_intersections,我会检索相交边界框的屏幕坐标并将其存储在 screen_object_center 中:

var width = window.innerWidth, height = window.innerHeight;
var widthHalf = width / 2, heightHalf = height / 2;
var screen_object_center = new THREE.Vector3();
var projector = new THREE.Projector();
projector.projectVector(screen_object_center.setFromMatrixPosition(INTERSECTED.matrixWorld), camera);
screen_object_center.x = (screen_object_center.x * widthHalf) + widthHalf;
screen_object_center.y = -(screen_object_center.y * heightHalf) + heightHalf;

一旦我有了 screen_object_center——相交对象或边界框中心的屏幕坐标——我就可以在鼠标悬停的任何地方放置一个偏移文本标签。

例如,下面是鼠标指针悬停在最中心粒子的边界框上时文本标签如何显示的快照:

Default view with label

我使用 id_label 边界框来调整标签的垂直偏移:

id_label.style.top = (screen_object_center.y - 0.85 * (id_label_rect.height / 2)) + 'px';

这没问题。

我的问题是如何设置标签相对于粒子位置和深度的水平偏移。

复杂的是较深的背景粒子比前景中的粒子小,因此我需要较小的水平偏移来使标签与其边缘保持恒定的距离。

我失败的尝试涉及计算一个名为 screen_object_edgeTHREE.Vector3,它试图获取粒子边界框的边缘(INTERSECTED 对象),而不是它的中心点。

通过检索对应于粒子边缘的屏幕坐标,我希望在粒子边缘和标签之间设置一个恒定的水平间隙,而不管粒子的深度如何。

我尝试通过复制 INTERSECTED 对象的矩阵,设置位于边界框 Angular 的新位置,然后投影一个名为 screen_object_edge 的向量从复制的矩阵位置到相机:

var INTERSECTED_matrix_copy = new THREE.Matrix4().copy(INTERSECTED.matrixWorld);
INTERSECTED_matrix_copy.setPosition(new THREE.Vector3(INTERSECTED.position.x + 0.0375, INTERSECTED.position.y + 0.0375, INTERSECTED.position.z + 0.0375));
var screen_object_edge = new THREE.Vector3();
projector.projectVector(screen_object_edge.setFromMatrixPosition(INTERSECTED_matrix_copy), camera);
screen_object_edge.x = (screen_object_edge.x * widthHalf) + widthHalf;
screen_object_edge.y = -(screen_object_edge.y * heightHalf) + heightHalf;

一旦我旋转场景,事情就中断了:标签在垂直位置设置正确,但在水平位置设置不正确,因为有些标签现在与它们的粒子边界框重叠。

这里是粒子云旋转后突出显示的相同标签的示例,如上例所示:

Default view, rotated and showing label

如果一切正常,标签将位于与旋转场景中的右粒子边缘相同的水平距离,就像在原始的、未旋转的默认场景中一样。在这里,标签与粒子重叠。

我的问题是如何计算screen_object_edge 以便我可以获得粒子边界框边缘的正确屏幕坐标,然后正确偏移文本标签的水平位置,相对于其各自粒子的左边缘或右边缘。

编辑

感谢 vals 提供正确答案。这是我修改后的 render() 函数的片段:

function render() {
    // ...
    var scene_right = new THREE.Vector3().crossVectors(raycaster.ray.origin, camera.up).setLength(0.0375);
    var bounding_box_intersections = raycaster.intersectObjects(bounding_boxes);
    if (bounding_box_intersections.length > 0) { 
        // ...
        var INTERSECTED_matrix_copy = new THREE.Matrix4().copy(INTERSECTED.matrixWorld);
        INTERSECTED_matrix_copy.setPosition(new THREE.Vector3(INTERSECTED.position.x + scene_right.x, INTERSECTED.position.y + scene_right.y, INTERSECTED.position.z + scene_right.z));
        var screen_object_edge = new THREE.Vector3();
        projector.projectVector(screen_object_edge.setFromMatrixPosition(INTERSECTED_matrix_copy), camera);
        screen_object_edge.x = (screen_object_edge.x * widthHalf) + widthHalf;
        screen_object_edge.y = -(screen_object_edge.y * heightHalf) + heightHalf;
        // ...
        if (mouse.x < 0)
            id_label.style.left = (screen_object_center.x - (screen_object_edge.x - screen_object_center.x)) + 'px';
        else {
            id_label.style.left = (screen_object_center.x + (screen_object_edge.x - screen_object_center.x) - id_label_rect.width) + 'px';
            id_label.style.textAlign = 'right';
        }
    }
}

当我旋转场景时,标签位置现在与边界框边缘保持恒定距离,无论边界框的 z 深度如何:

Revised edge vector, default position

Revised edge vector, rotated position

最佳答案

我认为解决方案应该是

  1. 你有边界框的中心
  2. 您计算光线转换器向量与相机中向上向量的叉积。此矢量应指向场景的右侧。
  3. 您将此矢量长度设置为边界框大小的一半
  4. 您将此向量添加到边界框的中心。

关于javascript - 在three.js中计算边界框水平边缘的屏幕坐标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27796045/

相关文章:

javascript - Canvas 问题

php - 数据库问题的 HTML 输出

javascript - Collada Material 不重复

javascript - 如何在 Three.js 场景中平滑多边形?

javascript - jQuery AJAX 的 if else 语句未运行

javascript - 如何在纯 Javascript 中的 PHP AJAX 调用中使用 header 重定向?

分配给 JS 对象的 Javascript Dom 元素 ID?我是疯了还是……?

javascript - 我如何创建交叉淡入淡出以便我可以使用 javascript 调用 css 中的关键帧?

jquery - 使用 CSS3 旋转时无法淡出

javascript - Three.js BufferGeometry 与粒子几何