我正在 Mapbox GL JS 页面中使用 Three.js 渲染一些自定义层,如下 this example .我想添加光线转换来确定用户点击了哪个对象。
问题是我只从 Mapbox 获得了一个投影矩阵,我用它来渲染场景:
class CustomLayer {
type = 'custom';
renderingMode = '3d';
onAdd(map, gl) {
this.map = map;
this.camera = new THREE.Camera();
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true,
});
this.scene = new THREE.Scene();
// ...
}
render(gl, matrix) {
this.camera.projectionMatrix = new THREE.Matrix4()
.fromArray(matrix)
.multiply(this.cameraTransform);
this.renderer.state.reset();
this.renderer.render(this.scene, this.camera);
}
}
这渲染得很好,并在我平移/旋转/缩放 map 时跟踪 View 中的变化。
不幸的是,当我尝试添加光线转换时,出现错误:
raycast(point) {
var mouse = new THREE.Vector2();
mouse.x = ( point.x / this.map.transform.width ) * 2 - 1;
mouse.y = 1 - ( point.y / this.map.transform.height ) * 2;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
console.log(raycaster.intersectObjects(this.scene.children, true));
}
这给了我一个异常(exception):
THREE.Raycaster: Unsupported camera type.
我可以从通用更改
THREE.Camera
到 THREE.PerspectiveCamera
在不影响场景渲染的情况下:this.camera = new THREE.PerspectiveCamera(28, window.innerWidth / window.innerHeight, 0.1, 1e6);
这修复了异常,但也不会导致记录任何对象。挖掘了一下发现相机的
projectionMatrixInverse
都是NaN
s,我们可以通过计算来修复它: raycast(point) {
var mouse = new THREE.Vector2();
mouse.x = ( point.x / this.map.transform.width ) * 2 - 1;
mouse.y = 1 - ( point.y / this.map.transform.height ) * 2;
this.camera.projectionMatrixInverse.getInverse(this.camera.projectionMatrix); // <--
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
console.log(raycaster.intersectObjects(this.scene.children, true));
}
现在无论我点击哪里,我都会得到两个交叉点,立方体的两个面。它们的距离为 0:
[
{ distance: 0, faceIndex: 10, point: Vector3 { x: 0, y: 0, z: 0 }, uv: Vector2 {x: 0.5, y: 0.5}, ... },
{ distance: 0, faceIndex: 11, point: Vector3 { x: 0, y: 0, z: 0 }, uv: Vector2 {x: 0.5, y: 0.5}, ... },
]
所以很明显有些东西在这里不起作用。看着code for
setCamera
,它涉及projectionMatrix
和 matrixWorld
.有没有办法设置matrixWorld
,或仅使用投影矩阵直接构建射线转换器的射线?似乎我只需要投影矩阵来渲染场景,所以我希望它也是我转换光线所需的全部。完整示例 in this codepen .
最佳答案
调试困难,但解决了!
TL;DR:此工作中的完整代码 Fiddle .
坐标系
DefaultUp
在 BoxCustomLayer
构造函数,因为目标是使所有内容与三中的默认坐标对齐。 onAdd()
中翻转 y 轴方法,但随后您还可以在初始化组时缩放所有对象。这样,您在反转投影后操作的坐标 raycast()
不再以米为单位。所以让我们用 this.cameraTransform
加入缩放部分.左手到右手的转换也应该在这里完成。我决定翻转 z 轴并沿 x 轴旋转 90 度以获得 y 向上的右手系:const centerLngLat = map.getCenter();
this.center = MercatorCoordinate.fromLngLat(centerLngLat, 0);
const {x, y, z} = this.center;
const s = this.center.meterInMercatorCoordinateUnits();
const scale = new THREE.Matrix4().makeScale(s, s, -s);
const rotation = new THREE.Matrix4().multiplyMatrices(
new THREE.Matrix4().makeRotationX(-0.5 * Math.PI),
new THREE.Matrix4().makeRotationY(Math.PI)); //optional Y rotation
this.cameraTransform = new THREE.Matrix4()
.multiplyMatrices(scale, rotation)
.setPosition(x, y, z);
makeScene
中的组中删除缩放。 ,实际上您不再需要该组了。 render
功能。 光线转换
这里有点棘手,基本上就是
Raycaster
可以,但我省略了一些不必要的函数调用,例如相机世界矩阵是身份 => 无需乘以它。Object3D.projectionMatrix
时不更新已分配,所以让我们手动计算它。 Vector3.unproject
. viewDirection
只是来自 cameraPosition
的归一化向量到 mousePosition
. const camInverseProjection =
new THREE.Matrix4().getInverse(this.camera.projectionMatrix);
const cameraPosition =
new THREE.Vector3().applyMatrix4(camInverseProjection);
const mousePosition =
new THREE.Vector3(mouse.x, mouse.y, 1)
.applyMatrix4(camInverseProjection);
const viewDirection = mousePosition.clone()
.sub(cameraPosition).normalize();
this.raycaster.set(cameraPosition, viewDirection);
完整代码
Fiddle用于演示。
mousemove
使用 jQuery 显示实时调试信息。 关于three.js - Three.js 中的 Raycast 只有一个投影矩阵,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59163141/