【发布时间】:2015-03-03 23:10:37
【问题描述】:
我有一个呈现一组粒子的 three.js 页面(可运行的演示和代码位于:https://dl.dropboxusercontent.com/u/31495717/cubemaker/index.html)。
这是默认视图的快照,我们基本上是在查看以 (0, 0, 0) 为中心的单位立方体:
我在这个单位立方体内的每个粒子的中心都有透明的边界框。如果我修改不透明度,我们可以看到每个粒子周围的框:
var bounding_box = new THREE.Mesh(
bounding_box_geometry,
new THREE.MeshLambertMaterial({
opacity: 0.5,
transparent: true,
alphaTest: 0.5
})
);
我使用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——相交对象或边界框中心的屏幕坐标——我可以在鼠标悬停的任何地方放置一个偏移的文本标签。
例如,下面是文本标签如何在鼠标指针悬停在最中心粒子的边界框上时显示的快照:
我使用id_label边界框来调整标签的垂直偏移:
id_label.style.top = (screen_object_center.y - 0.85 * (id_label_rect.height / 2)) + 'px';
这没问题。
我的问题是如何设置标签的水平偏移量,相对于粒子的位置和深度。
复杂之处在于,较深的背景粒子比前景中的粒子小,因此我需要较小的水平偏移来使标签与边缘保持恒定距离。
我失败的尝试涉及计算一个名为screen_object_edge 的THREE.Vector3,它试图获取粒子边界框(INTERSECTED 对象)的边缘,而不是其中心点。
通过检索与粒子边缘对应的屏幕坐标,我希望在粒子边缘和标签之间设置一个恒定的水平间隙,而不管粒子的深度如何。
我通过复制INTERSECTED 对象的矩阵来尝试此操作,设置位于边界框角落的新位置,然后将一个名为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;
一旦我旋转场景,事情就会中断:标签在垂直位置正确设置,但在水平位置设置不正确,因为现在有些标签与粒子的边界框重叠。
这是一个高亮标签的示例,与前面的示例一样,在粒子云旋转后:
如果一切正常,标签将与旋转场景中的右侧粒子边缘保持相同的水平距离,就像在原始的未旋转默认场景中一样。在这里,标签与粒子重叠。
我的问题是如何计算screen_object_edge,这样我才能得到粒子边界框边缘的正确屏幕坐标,然后正确偏移文本标签的水平位置,相对于左边缘或右边缘其各自的粒子。
编辑
感谢 vals 的正确答案。这是我修改后的render() 函数的 sn-p:
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 深度无关:
【问题讨论】:
标签: javascript html three.js