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

SelectionBox API 案例使用

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


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);
}


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

// 当窗口大小改变时调整相机的纵横比和渲染器的大小
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);
    }
});


全部源码


<!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>



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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/273712.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Spring Boot的宿舍管理系统

摘 要 随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建宿舍管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种楼宇信息、宿舍信息、宿舍安排、缺勤信息…

【Linux】进程---概念---进程---优先级

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;Linux_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.操作系统(Operator System) 1.1 概念 1.2 设计OS的目的 1.3 定位 1.4 如何理解 "管理" 1.5 总结 1.6 系统调用和…

【Python】进阶学习:一文解决如何从指定的源目录中,挑选出符合条件的文件,并将这些文件复制到目标目录中

【Python】进阶学习&#xff1a;一文解决如何从指定的源目录中&#xff0c;挑选出符合条件的文件&#xff0c;并将这些文件复制到目标目录中 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化…

Legacy|电脑Windows系统如何迁移到新安装的硬盘?系统迁移详细教程!

前言 前面讲了很多很多关于安装系统、重装系统的教程。但唯独没有讲到电脑换了新的硬盘之后&#xff0c;怎么把旧系统迁移到新的硬盘上。 今天小白就来跟各位小伙伴详细唠唠&#xff1a; 开始之前需要把系统迁移的条件准备好&#xff0c;意思就是在WinPE系统下&#xff0c;可…

如何查看MySQL数据库的连接数

连接数是指用户已经创建多少个连接&#xff0c;也就是MySQL中通过执行 SHOW PROCESSLIST命令输出数据库中运行着的线程个数的详情&#xff0c;如图6-1-1所示。 SHOW PROCESSLIST默认情况下只显示前100条记录的详情&#xff0c;如果需要显示超过100条的所有记录&#xff0c;可以…

[C语言]指针详解一、数组指针、二维数组传参、函数指针

一、数组指针 对一个数组&#xff0c;如果我们想要让一个指针指向这个数组&#xff0c;我们应该如何定义呢?我们知道一个数组定义本来就是一个指针&#xff0c;那为何要多定义一个数组指针呢?我们来看看下面这个代码就理解了 #include <stdio.h> int main() {int arr…

【源码阅读】evmⅠ

代码位置如下&#xff1a; 参考link 以太坊中有一个很重要的用途是智能合约&#xff0c;而其中evm模块是实现了执行智能合约的虚拟机。evm可以逐条解析执行智能合约的指令。 evm中的核心对象是EVM&#xff0c;代表一个以太坊虚拟机。其内部主要依赖&#xff1a;解释器Interore…

高通 8255 基本通信(QUP)Android侧控制方法说明

一&#xff1a;整体说明 高通8255芯片中&#xff0c;SPI IIC UART核心统一由QUP V3 进行控制 QUP V3为可编程模块&#xff0c;可以将不同通道配置为SPI IIC UART通路&#xff0c;此部分配置在QNX侧 QUP 资源可以直接被QNX使用&#xff0c;Android侧可以通过两种方法使用QUP资源…

C++开发基础——类对象与构造析构

一、基础概念 类&#xff1a;用户自定义的数据类型。 对象&#xff1a;类类型的变量&#xff0c;类的实例。 类的成员&#xff1a;成员变量和成员函数。 成员变量&#xff1a;类中定义的变量。 成员函数&#xff1a;类中定义的函数。 定义类的代码样例&#xff1a; class…

CentOS7使用Docker部署.net Webapi

1 准备WebApi项目 对于已存在的WebApi项目&#xff0c;需要添加Docker支持&#xff1b; 编码时&#xff0c;先设置好项目需要的端口号&#xff1a;program.cs中&#xff0c;app.Run("http://*:8000");设置端口为&#xff1a;8000在VS中&#xff0c;选中项目&#xf…

汽车功能安全整体方法

摘 要 ISO26262道路车辆功能安全标准已经制定实践了多年&#xff0c;主要目标是应对车辆的电子和电气&#xff08;E/E&#xff09;系统失效。该方法践行至今&#xff0c;有些系统功能安全方法已经成熟&#xff0c;例如电池管理系统&#xff08;BMS&#xff09;&#xff0c;并且…

idea 开发serlvet班级通讯录管理系统idea开发mysql数据库web结构计算机java编程layUI框架开发

一、源码特点 idea开发 java servlet 班级通讯录管理系统是一套完善的web设计系统mysql数据库 系统采用serlvetdaobean mvc 模式开发&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 servlet 班…

阿里云ecs服务器配置反向代理上传图片

本文所有软件地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/12OSFilS-HNsHeXTOM47iaA 提取码&#xff1a;dqph 为什么要使用阿里云服务器&#xff1f; 项目想让别人通过外网进行访问就需要部署到我们的服务器当中 1.国内知名的服务器介绍 国内比较知名的一些…

spring 没完没了

start 轻量级开源的j2ee框架&#xff0c;容器框架 装javabean aop ioc 定义一个starter的jar包&#xff0c;写一个configuration配置类&#xff0c;将bean定义其中&#xff0c;在starter包的meta-inf/spring.factories中写入配置类&#xff0c;springboot会按约定加载该配置类 …

Excel xlsx file:not supported

报错信息&#xff1a; 原因&#xff1a; Excel和xlrd版本不匹配 解决措施&#xff1a; 降低xlrd版本或Excel版本 方法一&#xff1a; 1) 先卸载了原来的版本&#xff1a;uninstal xlrd 2) 安装新的低版本xlrd模块pip install xlrd1.2.0 方法二&#xff1a; 1&#xff09…

鸿蒙Harmony应用开发—ArkTS声明式开发(绘制组件:Rect)

矩形绘制组件。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Rect(value?: {width?: string | number,height?: string | number,radius?: string | number | Array<s…

springboot+poi-tl根据模板导出word(含动态表格和图片),并将导出的文档压缩zip导出

springbootpoi-tl根据模板导出word&#xff08;含动态表格和图片&#xff09; 官网&#xff1a;http://deepoove.com/poi-tl/ 参考网站&#xff1a;https://blog.csdn.net/M625387195/article/details/124855854 pom导入的maven依赖 <dependency><groupId>com.dee…

JavaScript之继承

继承 父类与子类 子类是父类的一个子集 比如&#xff1a;人类和医生类&#xff0c;医生类是人类的子集&#xff1b;人类是父类&#xff0c;医生类是子集 父类与子类在特性&#xff08;属性和方法&#xff09;上有什么关系 方法&#xff1a;子类对象可以调用父类原型上的方…

MySQL---索引

一、索引概述 索引&#xff08;index&#xff09;是帮助MySQL高效获取数据的数据结构(有序)。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数据&#xff0c; 这样就可以在这些数据…

【Unity动画】Unity如何导入序列帧动画(GIF)

Unity 不支持GIF动画的直接播放&#xff0c;我们需要使用序列帧的方式 01准备好序列帧 02全部拖到Unity 仓库文件夹中 03全选修改成精灵模式Sprite 2D ,根据需要修改尺寸&#xff0c;点击Apply 04 创建一个空物体 拖动序列上去 然后全选所有序列帧&#xff0c;拖到这个空物体…
最新文章