ThreeJS-3D教学七-交互

在threejs中想要选中一个物体,点击或者鼠标悬浮,又或者移动端的touch事件,核心都是通过new THREE.Raycaster完成的。这里用到了一个概念,即我们点击时的 屏幕坐标 转换为 three中的3D坐标。
先看效果图:
在这里插入图片描述
代码是:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>body {width: 100%;height: 100%;}* {margin: 0;padding: 0;}.label {font-size: 20px;color: #000;font-weight: 700;}</style>
</head>
<body>
<div id="container"></div>
<script type="importmap">{"imports": {"three": "../three-155/build/three.module.js","three/addons/": "../three-155/examples/jsm/"}}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GPUStatsPanel } from 'three/addons/utils/GPUStatsPanel.js';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
let stats, labelRenderer, gpuPanel, temporaryKeep;
let camera, scene, renderer, controls;
const group = new THREE.Group();
let widthImg = 200;
let heightImg = 200;
const mouse = new THREE.Vector2();
init();
initHelp();
initLight();
axesHelperWord();
animate();
// 添加平面
addPlane();let geometry = new THREE.BoxGeometry(20, 20, 10);
let material = new THREE.MeshLambertMaterial({color: 0xcccccc});
let cube = new THREE.Mesh(geometry, material);
cube.position.y = 10;
cube.name = 'BoxGeometry';
scene.add(cube);/*** CylinderGeometry(radiusTop : Float, radiusBottom : Float, height : Float, radialSegments : Integer, heightSegments : Integer)radiusTop—顶部圆柱体的半径。默认值为1。radiusBottom—底部圆柱体的半径。默认值为1。height——圆柱体的高度。默认值为1。radialSegments—圆柱体圆周上的分段面数。默认值为32heightSegments—沿圆柱体高度的面行数。默认值为1。*/
let geometry1 = new THREE.CylinderGeometry(15, 15, 10, 32, 1);
let material1 = new THREE.MeshLambertMaterial({color: 0xffff00});
let cylinder = new THREE.Mesh(geometry1, material1);
cylinder.position.set(30, 5, -50);
cylinder.name = 'CylinderGeometry';
scene.add(cylinder);function onDocumentMouseMove(event) {event.preventDefault();// 将鼠标点击位置的屏幕坐标转成threejs中的标准坐标,具体解释见代码释义 如果 canvas有左边距 和 上边距 需要减去mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;// 新建一个三维单位向量 假设z方向就是1// 根据照相机,把这个向量转换到视点坐标系const vector = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera);// 在视点坐标系中形成射线,射线的起点向量是照相机, 射线的方向向量是照相机到点击的点,这个向量应该归一标准化。const raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());//射线和模型求交,选中一系列直线const intersects = raycaster.intersectObjects(scene.children);console.log('imtersrcts=', intersects);/*** 不是所有的 Material 都有这个属性* emissive.getHex* emissive.setHex*/if (intersects.length > 0) {//选中第一个射线相交的物体if (temporaryKeep !== intersects[0].object) {if (temporaryKeep) temporaryKeep.material.emissive?.setHex(temporaryKeep.currentHex);temporaryKeep = intersects[0].object;temporaryKeep.currentHex = temporaryKeep.material.emissive?.getHex();temporaryKeep.material.emissive?.setHex(0xff0000);}} else {if (temporaryKeep) temporaryKeep.material.emissive?.setHex(temporaryKeep.currentHex);temporaryKeep = null;}
}function addPlane() {// 创建一个平面 PlaneGeometry(width, height, widthSegments, heightSegments)const planeGeometry = new THREE.PlaneGeometry(widthImg, heightImg, 1, 1);// 创建 Lambert 材质:会对场景中的光源作出反应,但表现为暗淡,而不光亮。const planeMaterial = new THREE.MeshPhongMaterial({color: 0xb2d3e6,side: THREE.DoubleSide});const plane = new THREE.Mesh(planeGeometry, planeMaterial);// 以自身中心为旋转轴,绕 x 轴顺时针旋转 45 度plane.rotation.x = -0.5 * Math.PI;plane.position.set(0, -4, 0);scene.add(plane);
}function init() {camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 10, 2000 );camera.up.set(0, 1, 0);camera.position.set(60, 40, 60);camera.lookAt(0, 0, 0);scene = new THREE.Scene();scene.background = new THREE.Color( '#ccc' );renderer = new THREE.WebGLRenderer( { antialias: true } );renderer.setPixelRatio( window.devicePixelRatio );renderer.setSize( window.innerWidth, window.innerHeight );document.body.appendChild( renderer.domElement );labelRenderer = new CSS2DRenderer();labelRenderer.setSize( window.innerWidth, window.innerHeight );labelRenderer.domElement.style.position = 'absolute';labelRenderer.domElement.style.top = '0px';labelRenderer.domElement.style.pointerEvents = 'none';document.getElementById( 'container' ).appendChild( labelRenderer.domElement );controls = new OrbitControls( camera, renderer.domElement );controls.mouseButtons = {LEFT: THREE.MOUSE.PAN,MIDDLE: THREE.MOUSE.DOLLY,RIGHT: THREE.MOUSE.ROTATE};controls.enablePan = true;// 设置最大最小视距controls.minDistance = 20;controls.maxDistance = 1000;window.addEventListener( 'resize', onWindowResize );stats = new Stats();stats.setMode(1); // 0: fps, 1: msdocument.body.appendChild( stats.dom );gpuPanel = new GPUStatsPanel( renderer.getContext() );stats.addPanel( gpuPanel );stats.showPanel( 0 );scene.add( group );document.addEventListener('click', onDocumentMouseMove, false);
}function initLight() {const light = new THREE.DirectionalLight(new THREE.Color('rgb(253,253,253)'));light.position.set(100, 100, -10);light.intensity = 3; // 光线强度light.castShadow = true; // 是否有阴影light.shadow.mapSize.width = 2048; // 阴影像素light.shadow.mapSize.height = 2048;// 阴影范围const d = 80;light.shadow.camera.left = -d;light.shadow.camera.right = d;light.shadow.camera.top = d;light.shadow.camera.bottom = -d;light.shadow.bias = -0.0005; // 解决条纹阴影的出现// 最大可视距和最小可视距light.shadow.camera.near = 0.01;light.shadow.camera.far = 2000;const AmbientLight = new THREE.AmbientLight(new THREE.Color('rgb(255, 255, 255)'));scene.add( light );scene.add( AmbientLight );
}function initHelp() {// const size = 100;// const divisions = 5;// const gridHelper = new THREE.GridHelper( size, divisions );// scene.add( gridHelper );// The X axis is red. The Y axis is green. The Z axis is blue.const axesHelper = new THREE.AxesHelper( 100 );scene.add( axesHelper );
}function axesHelperWord() {let xP = addWord('X轴');let yP = addWord('Y轴');let zP = addWord('Z轴');xP.position.set(50, 0, 0);yP.position.set(0, 50, 0);zP.position.set(0, 0, 50);
}function addWord(word) {let name = `<span>${word}</span>`;let moonDiv = document.createElement( 'div' );moonDiv.className = 'label';// moonDiv.textContent = 'Moon';// moonDiv.style.marginTop = '-1em';moonDiv.innerHTML = name;const label = new CSS2DObject( moonDiv );group.add( label );return label;
}function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize( window.innerWidth, window.innerHeight );
}function animate() {requestAnimationFrame( animate );stats.update();controls.update();labelRenderer.render( scene, camera );renderer.render( scene, camera );
}
</script>
</body>
</html>

具体思路代码中都有注释,实在不懂也没关系,函数一封装先用着就是,后面慢慢理解!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023全国大学生软件测试大赛开发者测试练习题99分答案(ScapegoatTree2023)

2023全国大学生软件测试大赛开发者测试练习题99分答案(ScapegoatTree2023) 题目详情题解代码(直接全部复制到test类中即可)提示:该题只需要分支覆盖得分即可,不需要变异得分 题目详情 题解代码(直接全部复制到test类中即可) package net.mooctest;import static org.…

Zabbix安装出现必要条件检查失败

问题描述 今天在某朋友部署新环境的Zabbix时&#xff0c;系统出现如下的检查失败情况。此环境的基础部分不是我负责&#xff0c;而是其它项目共存的PHP环境&#xff0c;也是挺奇怪的。一般来说&#xff0c;不应该将zabbix与其它系统部署在一起&#xff0c;没有条件哪怕时Docke…

在服务器上解压.7z文件

1. 更新apt sudo apt-get update2. 安装p7zip sudo apt-get install p7zip-full3. 解压.7z文件 7za x WN18RR.7z

OpenCV4(C++)—— 直方图

文章目录 前言一、计算直方图二、归一化三、直方图均衡化四、直方图匹配 前言 直方图(Histogram)最开始在统计学中被提出&#xff0c;由一系列高度不等的纵向条纹或线段表示数据分布的情况。 一般用横轴表示数据类型&#xff0c;纵轴表示分布情况。在图像领域&#xff0c;直方…

旅游网站HTML

代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>旅游网</title> </head> <body><!--采用table编辑--> <!--最晚曾table,用于整个页面那布局--><table width&q…

Java I/O 的 OutputStream 输出流相关知识点详解

Java 17 的 I/O 基础 OutputStream 篇 对于 OutputStream 主要是字节流类型的输出流。 OutputStream OutputStream 抽象类是所有字节输出流类的超类。输出流接受输出字节并将它们发送到某个接收器中。 同样该抽象类需要一个子类来继承实现始终提供至少一种写入一个字节输出的…

es6(二)——常用es6说明

ES6的系列文章目录 es6&#xff08;一&#xff09;——var和let和const的区别 文章目录 ES6的系列文章目录一、变量的结构赋值1.数组的结构赋值2.对象的结构赋值 二、模板字符串三、扩展运算符1.字符串的使用2.数组的使用 四、箭头函数1.普通函数的定义2.箭头函数的定义3.箭头…

简单大方的自我介绍 PPT 格式

自我介绍是展示自己的机会&#xff0c;同时也是展现自信和魅力的重要时刻。通过简单大方的PPT格式&#xff0c;可以更好地展示自己的个性和才华。下面是一些建议&#xff0c;帮助你在自我介绍中展现自信和魅力。 1. 打造简洁而有吸引力的PPT布局&#xff1a; - 选择简洁大方的背…

数据库常见面试题--MySQL

梳理面试过程中数据库相关的常见问题&#xff0c;需要说明的是&#xff0c;这篇文章主要是基于MySQL数据库&#xff0c;其他类型的数据库还请自行参考使用。 数据库概述 为什么使用数据库 1、数据库增删改查更方便 2、提供了事务的能力 本质是更好的管理数据。 数据库体系结…

(部署服务器系列一)虚拟机模拟部署服务器

1、下载安装vmware 15 &#xff08;win7最高支持版&#xff09; 2、下载安装CentOS 配置2核2g&#xff08;最少&#xff09;磁盘100g&#xff08;不会实际占有&#xff09;选择时区-上海配置分区&#xff1a;https://blog.csdn.net/qq_35363507/article/details/127390889 &a…

借助PLC-Recorder,汇川中型PLC(AM、AC系列,CODESYS平台)2ms高速采集的方法

高速数据采集要保证速度&#xff0c;也要保证时刻的准确性。在windows系统里&#xff0c;时间稳定性是个很难的问题。如果PLC发送的数据里带有时间信息&#xff0c;则可以由PLC来保证采样周期的稳定性。 从V2.12版本开始&#xff0c;PLC-Recorder软件可以处理发送电文里的时间…

Ubuntu输入正确密码重新跳到登录界面

Ubuntu输入正确密码重新跳到登录界面 问题描述 输入正确的密码登录后闪一下又回到锁屏界面 输入正确的密码后还是回到这个界面 产生的原因 /etc/profile或者/etc/enviroment出现了问题,导致无法正常登录 该错误产生的原因不止一个 这里是因为/etc/profile或者/etc/enviromen出…

收银系统商品定价设计思考

一、背景 因为门店系统里商品总共也就几万款&#xff0c;一直以来都是根据条码由总部统一定价销售&#xff0c;现在有加盟店&#xff0c;各门店也有进行各自促销活动的需求&#xff0c;这就需要放开门店自主定价权&#xff0c;所以近段时间系统在商品定价上做了扩展。 二、商…

如何在雷电模拟器上安装Magisk并加载movecert模块抓https包(一)

环境&#xff1a;win10 64位&#xff0c;雷电模拟器版本4.0.78&#xff0c;Android版本7.1.2。 前几天写了一篇文章如何在逍遥模拟器上加载Magisk模块-CSDN博客&#xff0c;因为最近很忙&#xff0c;所以直到今天才有空写这一篇&#xff0c;记录如何在雷电模拟器上安装Magisk并…

基于uniapp的商城外卖小程序

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

LCD12864驱动开发

目录 一、概述 二、方框图 三、模块接口说明 1、串口接口管脚信号 2、并行接口 四、模块主要硬件构成说明 1、RS&#xff0c;R/W配4种模式&#xff1a; 2、E信号 五、指令说明 六、读写时序图 6.1 数据传输过程 6.2、时序图 6.3、串口读写时序 七、交流参数 八、软件…

TCP/IP(十五)拥塞控制

一 拥塞控制 ① 拥塞控制必要性 思考&#xff1a; 为什么要有拥塞控制呀,不是有流量控制了吗&#xff1f; ② 拥赛窗口 cwnd 什么是拥塞窗口? 和发送窗口有什么关系呢?明白&#xff1a; cwnd、swnd、rwnd 缩写 含义 ③ 如何知道当前网络是否出现了拥塞呢&#xff1f;…

2023年【危险化学品生产单位安全生产管理人员】及危险化学品生产单位安全生产管理人员模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品生产单位安全生产管理人员考前必练&#xff01;安全生产模拟考试一点通每个月更新危险化学品生产单位安全生产管理人员模拟考试题题目及答案&#xff01;多做几遍&#xff0c;其实通过危险化学品生产单位安…

微信开发工具构建npm and git切换分支

目录 git切换分支NPM构建 git切换分支 案例&#xff1a; 再次查看分支就会发现自己的分支已切换&#xff0c;然后需要重新构建NPM一次 NPM构建 记得安装一下这个&#xff0c;然后在构建 如果未安装NPM&#xff0c;这时候需要打开命令端&#xff0c;安装操作&#xff0c;操作…

Configuration Change派发到App进程

整体时序 // DisplayContent.java boolean updateDisplayOverrideConfigurationLocked(Configuration values,ActivityRecord starting, boolean deferResume,ActivityTaskManagerService.UpdateConfigurationResult result) {int changes 0;boolean kept true;mAtmService.d…