更新于 

How to Create Objects on MouseClick?鼠标点击创建物体

实现原理

Threejs中不提供获取鼠标在空间中位置的api
因此作者研究出的方法如图所示:

需要两个步骤:

  • 根据相机位置,创建一个一直正面对着相机的平面

    • 以相机和原点连线为法线,原点为交点,创建平面
    • 使用threejs提供的setFromNormalAndCoplanarPoint方法
  • 根据鼠标和屏幕的交点,判断点击坐标

    • 获取相机和鼠标连线与辅助平面的交点,即鼠标点击坐标

代码实现

坐标计算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const mouse = new THREE.Vector2()
const planeNormal = new THREE.Vector3()
const plane = new THREE.Plane()
const raycaster = new THREE.Raycaster()
const interSectionPoint = new THREE.Vector3()
window.onmousemove = function(e){
mouse.x = e.offsetX/window.innerWidth*2 - 1
mouse.y = 1- e.offsetY/window.innerHeight*2
// 创建辅助平面
planeNormal.copy(camera.position)
plane.setFromNormalAndCoplanarPoint(planeNormal, scene.position)
// 射线交点
raycaster.setFromCamera(mouse,camera)
raycaster.ray.intersectPlane(plane, interSectionPoint)
}
物体添加
1
2
3
4
5
6
7
8
9
10
11
window.onclick = function(e){
const sphereGeo = new THREE.SphereGeometry(0.2)
const sphereMat = new THREE.MeshStandardMaterial({
color:0xffffff*Math.random(),
metalness:0,
roughness:0
})
const sphereMesh = new THREE.Mesh(sphereGeo,sphereMat)
sphereMesh.position.copy(interSectionPoint)
scene.add(sphereMesh)
}

非全屏的raycaster坐标计算

全屏时,画布的宽高取的是:

  • window.innerWidth
  • window.innerHeight

非全屏时,需要将大部分涉及到宽高的值替换成:

  • dom.clientWidth
  • dom.clientHeight
1
2
3
4
5
6
7
8
9
10
11
12
// 鼠标射线
const mousePosition = new THREE.Vector2()
const raycast = new THREE.Raycaster()
window.onmousedown = e => {
mousePosition.x = e.offsetX/renderBox.clientWidth*2 -1
mousePosition.y = 1- e.offsetY/renderBox.clientHeight*2
raycast.setFromCamera(mousePosition, camera)
const intersections = raycast.intersectObjects(scene.children)
if (intersections[0] && intersections[0].object.isObject3D) {
intersections[0].object.layers.toggle(BLOOM_LAYER)
}
}