threejs 实现鼠标大面积选取场景内3d模型,SelectionBox API 案例使用

SelectionBox API 案例使用

这个函数创建了一个 3D 场景,包括一个相机、光源、多个立方体以及一个 WebGL 渲染器,并在页面上渲染这个场景

js 复制代码
function init() {
    // 创建一个容器 div 元素并将其添加到页面的 body 中
    container = document.createElement('div');
    document.body.appendChild(container);

    // 创建透视相机,并设置位置
    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 500);
    camera.position.z = 50;

    // 创建场景,并设置背景颜色
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf0f0f0);

    // 添加环境光
    scene.add(new THREE.AmbientLight(0xaaaaaa));

    // 创建聚光灯,并设置位置、角度和阴影属性
    const light = new THREE.SpotLight(0xffffff, 10000);
    light.position.set(0, 25, 50);
    light.angle = Math.PI / 5;

    light.castShadow = true;
    light.shadow.camera.near = 10;
    light.shadow.camera.far = 100;
    light.shadow.mapSize.width = 1024;
    light.shadow.mapSize.height = 1024;

    scene.add(light);

    // 创建立方体几何体
    const geometry = new THREE.BoxGeometry();

    // 创建 200 个立方体对象,并设置其位置、旋转、缩放以及阴影属性,并将其添加到场景中
    for (let i = 0; i < 200; i++) {
        const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff }));

        object.position.x = Math.random() * 80 - 40;
        object.position.y = Math.random() * 45 - 25;
        object.position.z = Math.random() * 45 - 25;

        object.rotation.x = Math.random() * 2 * Math.PI;
        object.rotation.y = Math.random() * 2 * Math.PI;
        object.rotation.z = Math.random() * 2 * Math.PI;

        object.scale.x = Math.random() * 2 + 1;
        object.scale.y = Math.random() * 2 + 1;
        object.scale.z = Math.random() * 2 + 1;

        object.castShadow = true;
        object.receiveShadow = true;

        scene.add(object);
    }

    // 创建 WebGL 渲染器,并设置像素比例、大小和阴影属性
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFShadowMap;

    // 将渲染器的 DOM 元素添加到容器中
    container.appendChild(renderer.domElement);

    // 创建性能监视器,并将其 DOM 元素添加到容器中
    stats = new Stats();
    container.appendChild(stats.dom);

    // 监听窗口大小变化事件,并调用 onWindowResize 函数
    window.addEventListener('resize', onWindowResize);
}

当窗口大小改变时调整相机的纵横比和渲染器的大小

js 复制代码
// 当窗口大小改变时调整相机的纵横比和渲染器的大小
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}

// 动画函数,用于更新场景并渲染帧

js 复制代码
function animate() {
    requestAnimationFrame(animate);
    render();
    stats.update(); // 更新性能监视器
}

// 渲染函数,用于渲染场景

js 复制代码
function render() {
    renderer.render(scene, camera);
}

// 创建一个选择框对象和一个选择辅助器对象

js 复制代码
const selectionBox = new SelectionBox(camera, scene);
const helper = new SelectionHelper(renderer, 'selectBox');

// 监听鼠标按下事件

js 复制代码
document.addEventListener('pointerdown', function(event) {
    // 将所有被选择的物体的发光效果重置为黑色
    for (const item of selectionBox.collection) {
        item.material.emissive.set(0x000000);
    }
    // 设置选择框的起始点坐标
    selectionBox.startPoint.set(
        (event.clientX / window.innerWidth) * 2 - 1,
        -(event.clientY / window.innerHeight) * 2 + 1,
        0.5
    );
});

// 监听鼠标移动事件

js 复制代码
document.addEventListener('pointermove', function(event) {
    // 如果选择辅助器处于按下状态
    if (helper.isDown) {
        // 将所有被选择的物体的发光效果重置为黑色
        for (let i = 0; i < selectionBox.collection.length; i++) {
            selectionBox.collection[i].material.emissive.set(0x000000);
        }
        // 设置选择框的结束点坐标
        selectionBox.endPoint.set(
            (event.clientX / window.innerWidth) * 2 - 1,
            -(event.clientY / window.innerHeight) * 2 + 1,
            0.5
        );
        // 执行选择框的选择操作,并获取所有被选择的物体
        const allSelected = selectionBox.select();
        // 将所有被选择的物体的发光效果设置为白色
        for (let i = 0; i < allSelected.length; i++) {
            allSelected[i].material.emissive.set(0xffffff);
        }
    }
});

// 监听鼠标松开事件

js 复制代码
document.addEventListener('pointerup', function(event) {
    // 设置选择框的结束点坐标
    selectionBox.endPoint.set(
        (event.clientX / window.innerWidth) * 2 - 1,
        -(event.clientY / window.innerHeight) * 2 + 1,
        0.5
    );
    // 执行选择框的选择操作,并获取所有被选择的物体
    const allSelected = selectionBox.select();
    // 将所有被选择的物体的发光效果设置为白色
    for (let i = 0; i < allSelected.length; i++) {
        allSelected[i].material.emissive.set(0xffffff);
    }
});

全部源码

js 复制代码
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - box selection</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
		<style>
			body {
				background-color: #f0f0f0;
				color: #000;
				touch-action: none;
			}

			a {
				color: #08e;
			}

			.selectBox {
				border: 1px solid #55aaff;
				background-color: rgba(75, 160, 255, 0.3);
				position: fixed;
			}
		</style>
	</head>
	<body>

		<div id="info">
			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - box selection
		</div>

		<script type="importmap">
			{
				"imports": {
					"three": "../build/three.module.js",
					"three/addons/": "./jsm/"
				}
			}
		</script>

		<script type="module">

			import * as THREE from 'three';

			import Stats from 'three/addons/libs/stats.module.js';

			import { SelectionBox } from 'three/addons/interactive/SelectionBox.js';
			import { SelectionHelper } from 'three/addons/interactive/SelectionHelper.js';

			let container, stats;
			let camera, scene, renderer;

			init();
			animate();

			function init() {

				container = document.createElement( 'div' );
				document.body.appendChild( container );

				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 500 );
				camera.position.z = 50;

				scene = new THREE.Scene();
				scene.background = new THREE.Color( 0xf0f0f0 );

				scene.add( new THREE.AmbientLight( 0xaaaaaa ) );

				const light = new THREE.SpotLight( 0xffffff, 10000 );
				light.position.set( 0, 25, 50 );
				light.angle = Math.PI / 5;

				light.castShadow = true;
				light.shadow.camera.near = 10;
				light.shadow.camera.far = 100;
				light.shadow.mapSize.width = 1024;
				light.shadow.mapSize.height = 1024;

				scene.add( light );

				const geometry = new THREE.BoxGeometry();

				for ( let i = 0; i < 200; i ++ ) {

					const object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );

					object.position.x = Math.random() * 80 - 40;
					object.position.y = Math.random() * 45 - 25;
					object.position.z = Math.random() * 45 - 25;

					object.rotation.x = Math.random() * 2 * Math.PI;
					object.rotation.y = Math.random() * 2 * Math.PI;
					object.rotation.z = Math.random() * 2 * Math.PI;

					object.scale.x = Math.random() * 2 + 1;
					object.scale.y = Math.random() * 2 + 1;
					object.scale.z = Math.random() * 2 + 1;

					object.castShadow = true;
					object.receiveShadow = true;

					scene.add( object );

				}

				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				renderer.shadowMap.enabled = true;
				renderer.shadowMap.type = THREE.PCFShadowMap;

				container.appendChild( renderer.domElement );

				stats = new Stats();
				container.appendChild( stats.dom );

				window.addEventListener( 'resize', onWindowResize );

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

			//

			function animate() {

				requestAnimationFrame( animate );

				render();
				stats.update();

			}

			function render() {

				renderer.render( scene, camera );

			}

			const selectionBox = new SelectionBox( camera, scene );
			const helper = new SelectionHelper( renderer, 'selectBox' );

			document.addEventListener( 'pointerdown', function ( event ) {

				for ( const item of selectionBox.collection ) {

					item.material.emissive.set( 0x000000 );

				}

				selectionBox.startPoint.set(
					( event.clientX / window.innerWidth ) * 2 - 1,
					- ( event.clientY / window.innerHeight ) * 2 + 1,
					0.5 );

			} );

			document.addEventListener( 'pointermove', function ( event ) {

				if ( helper.isDown ) {

					for ( let i = 0; i < selectionBox.collection.length; i ++ ) {

						selectionBox.collection[ i ].material.emissive.set( 0x000000 );

					}

					selectionBox.endPoint.set(
						( event.clientX / window.innerWidth ) * 2 - 1,
						- ( event.clientY / window.innerHeight ) * 2 + 1,
						0.5 );

					const allSelected = selectionBox.select();

					for ( let i = 0; i < allSelected.length; i ++ ) {

						allSelected[ i ].material.emissive.set( 0xffffff );

					}

				}

			} );

			document.addEventListener( 'pointerup', function ( event ) {

				selectionBox.endPoint.set(
					( event.clientX / window.innerWidth ) * 2 - 1,
					- ( event.clientY / window.innerHeight ) * 2 + 1,
					0.5 );

				const allSelected = selectionBox.select();

				for ( let i = 0; i < allSelected.length; i ++ ) {

					allSelected[ i ].material.emissive.set( 0xffffff );

				}

			} );

		</script>

	</body>
</html>

本内容来源于小豆包,想要更多内容请跳转小豆包 》

相关推荐
小彭努力中12 小时前
141. Sprite标签(Canvas作为贴图)
前端·深度学习·3d·webgl·three.js
Tianwen_Burning15 小时前
halcon3D 1:1切片轮廓投影技术,透过像素距离,知实际物体的尺寸
算法·3d
Struart_R15 小时前
Edify 3D: Scalable High-Quality 3D Asset Generation 论文解读
人工智能·深度学习·3d·扩散模型·三维生成·三维资产
mirrornan15 小时前
3D可视化产品定制,打造“所见即所得”的购物体验!
3d·3d模型·三维建模·3d可视化定制
卧式纯绿1 天前
自动驾驶3D目标检测综述(三)
人工智能·python·深度学习·目标检测·3d·cnn·自动驾驶
Struart_R1 天前
Epipolar-Free 3D Gaussian Splatting for Generalizable Novel View Synthesis 论文解读
人工智能·深度学习·计算机视觉·3d·transformer·三维重建·新视角生成
恬静的小魔龙1 天前
【SKFramework框架核心模块】3-2、音频管理模块
3d·unity·编辑器·游戏引擎·音视频
jndingxin2 天前
OpenCV相机标定与3D重建(3)校正鱼眼镜头畸变的函数calibrate()的使用
opencv·3d
前端Hardy2 天前
HTML&CSS:比赛记分卡
前端·javascript·css·3d·html
魔珐科技2 天前
当产业经济插上“数字羽翼”,魔珐有言AIGC“3D视频创作大赛”成功举办
3d·aigc·音视频