1、支持界面点选
点选模型整体思路是:根据camera位置作为起始点,叠加鼠标相对位置作为偏置,摄像头方向作为射线方向。
根据射线方向中的遇到的3D物体列表,第一个遇到的物体作为被点选的物体。
javascript
// 鼠标事件处理
let selectedObject = null;
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const rayHelper = new THREE.Line(new THREE.BufferGeometry(), new THREE.LineDashedMaterial({ color: 0xff0000, dashSize: 1, gapSize: 1 }));
scene.add(rayHelper);
function onMouseClick(event) {
event.preventDefault();
mouse.x = ((event.clientX / renderer.domElement.clientWidth) * 2) - 1;
mouse.y = -((event.clientY / renderer.domElement.clientHeight) * 2) + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
selectedObject = intersects[0].object;
const selectionInfo = document.getElementById('selection-info');
selectionInfo.innerHTML = `选中对象: ${selectedObject.name}`;
const origin = new THREE.Vector3();
origin.copy(camera.position);
const direction = new THREE.Vector3();
direction.copy(raycaster.ray.direction).multiplyScalar(100);
rayHelper.geometry.setFromPoints([origin, origin.clone().add(direction)]);
} else {
selectedObject = null;
const selectionInfo = document.getElementById('selection-info');
selectionInfo.innerHTML = '未选中任何对象';
rayHelper.geometry.dispose();
rayHelper.geometry = new THREE.BufferGeometry();
}
}
window.addEventListener('click', onMouseClick);
2、整体代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js 几何体操作示例</title>
<style>
body { margin: 0; overflow: hidden; }
#camera-info {
position: absolute;
top: 10px;
left: 10px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 10px;
font-family: Arial, sans-serif;
}
</style>
</head>
<body>
<div id="camera-info"></div>
<div id="selection-info"></div>
<script type="importmap">
{
"imports": {
"three": "./three.js-master/build/three.module.js",
"three/addons/": "./three.js-master/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three"
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { PCDLoader } from "three/addons/loaders/PCDLoader.js"
import { GLTFLoader } from "three/addons/loaders/GLTFloader.js"
// 1) 创建画布
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0xa0a0a0 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 2) 设置 camera 位置,朝向角度
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 20); // 设置相机位置
camera.lookAt(scene.position); // 让相机朝向场景中心
// 设置控制轨道
const controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 0.1, 0 );
controls.update();
controls.minDistance = 0.5;
controls.maxDistance = 1000;
controls.maxPolarAngle = 0.5 * Math.PI;
// 5) 支持动态显示摄像头位置、角度、缩放信息
const cameraInfo = document.getElementById('camera-info');
function updateCameraInfo() {
cameraInfo.innerHTML = `
摄像头信息:<br>
位置: (${camera.position.x.toFixed(2)}, ${camera.position.y.toFixed(2)}, ${camera.position.z.toFixed(2)})<br>
角度: (${camera.rotation.x.toFixed(2)}, ${camera.rotation.y.toFixed(2)}, ${camera.rotation.z.toFixed(2)})<br>
缩放: ${camera.zoom.toFixed(2)}
`;
}
updateCameraInfo();
// 渲染循环
function animate() {
requestAnimationFrame(animate);
updateCameraInfo();
renderer.render(scene, camera);
}
// 存储创建的几何体列表
createSphere()
// 几何体创建函数
// 创建球体
function createSphere() {
const geometry = new THREE.SphereGeometry( 1, 32, 32 );
const material = new THREE.MeshBasicMaterial( {color: 0x0000ff} );
const sphere = new THREE.Mesh( geometry, material );
sphere.name = 'Sphere';
sphere.position.set(0,0,0)
scene.add( sphere );
return sphere;
}
// 1) 导入点云
const loader = new PCDLoader();
loader.load( './exported_point_cloud.pcd', function ( points ) {
// loader.load( './three.js-master/examples/models/pcd/binary/Zaghetto.pcd', function ( points ) {
points.geometry.center();
points.geometry.rotateX( Math.PI );
points.name = 'Zaghetto.pcd';
points.position.set(2,0,0)
scene.add( points );
} );
// 1) 导入glb模型
const glb_loader = new GLTFLoader();
glb_loader.load('./three.js-master/examples/models/gltf/Horse.glb', (gltf) => {
// loader.load('./exported_model.gltf', (gltf) => {
const model = gltf.scene;
model.name = "Horse.glb"
scene.add(model);
// 2) 支持缩放模型比例
model.scale.set(1, 1, 1);
// 3) 支持调整模型放置位置,角度姿态
model.position.set(0, 0, 0);
model.rotation.set(0, 0, 0);
}, undefined, (error) => {
console.error('模型加载失败: ', error);
});
animate();
// 鼠标事件处理
let selectedObject = null;
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const rayHelper = new THREE.Line(new THREE.BufferGeometry(), new THREE.LineDashedMaterial({ color: 0xff0000, dashSize: 1, gapSize: 1 }));
scene.add(rayHelper);
function onMouseClick(event) {
event.preventDefault();
mouse.x = ((event.clientX / renderer.domElement.clientWidth) * 2) - 1;
mouse.y = -((event.clientY / renderer.domElement.clientHeight) * 2) + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
selectedObject = intersects[0].object;
const selectionInfo = document.getElementById('selection-info');
selectionInfo.innerHTML = `选中对象: ${selectedObject.name}`;
const origin = new THREE.Vector3();
origin.copy(camera.position);
const direction = new THREE.Vector3();
direction.copy(raycaster.ray.direction).multiplyScalar(100);
rayHelper.geometry.setFromPoints([origin, origin.clone().add(direction)]);
} else {
selectedObject = null;
const selectionInfo = document.getElementById('selection-info');
selectionInfo.innerHTML = '未选中任何对象';
rayHelper.geometry.dispose();
rayHelper.geometry = new THREE.BufferGeometry();
}
}
window.addEventListener('click', onMouseClick);
animate();
</script>
</body>
</html>
代码中为了方面确定鼠标射线,在点选后,我这边通过rayHelper显示点选射线方向。实际应用时可以删除。