threeJS射线拾取机制及案例
前言
在浏览器中浏览三维图形的时候,有时想要与三维图形之间做一些点击事件和交互操作,其中比较常用的一个解决方案就是使用Raycaster对象来实现(射线拾取)。
基础知识
- 世界坐标系:webGL中,世界坐标系是以屏幕中心为原点(0, 0, 0),且是始终不变的。面对屏幕时,右边是x正轴,上面是y正轴,由屏幕内指向屏幕外的是z正轴。
- 屏幕坐标系:webGL的重要功能之一就是将三维的世界坐标经过变换、投影等计算,最终计算出它在显示设备上对应的位置,这个位置就称为设备坐标,也就是二维坐标。
- 视点坐标系: 是以视点(照相机)为原点,以视线的方向为Z轴正方向的坐标系。
原理
利用webGL,既可以将三维坐标换算为二维坐标,也可以将二维坐标换算成三维坐标。webGL会将世界坐标先换算到视点坐标,然后进行裁剪,只有在视线范围之内的场景才会进入下一阶段的计算。
效果图1
- 图中圆柱体是演示拾取的三维图形;
- 图中绿、红、蓝三条轴线分别代表三维坐标中的Y正轴、X正轴和Z正轴;
- 图中蓝色五角形,其实是由视点(照相机)坐标出发,以鼠标点击位置为正方向,绘制的一条箭头图形,代表绘制的射线,五角形中心即鼠标点击位置。
当鼠标点击圆柱体边缘以外时,圆柱体材质颜色没有发生变化,说明此时鼠标点击位置并没有拾取到圆柱体。
效果图2
当鼠标点击圆柱体边缘以外时,圆柱体材质颜色发生了变化,说明此时鼠标点击位置已经拾取到了圆柱体,并在控制台打印出了圆柱体的mesh对象,并可以对它进行操作。
演示代码
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>钉钉分享</title> <!--threejs版本是71版本-->
<script src="three.js"></script>
</head> <body style="margin: 0;overflow: hidden;"> <!-- 输出容器 -->
<div id="webgl-output"></div> <!-- 操作代码 -->
<script> // 声名Threejs对象(场景、相机、渲染器)
var scene, camera, renderer; // 初始化
function init() { // 添加浏览器窗口大小监听事件(画布自适应方法onResize)
window.addEventListener('resize', onResize, false); // 鼠标点击拾取
document.addEventListener('click', initRay, false); // 创建场景
scene = new THREE.Scene(); // 创建相机(设置相机位置,设置相机朝向)
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(10, 50, 50);
camera.lookAt(scene.position); // 创建渲染器
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xEEEEEE);
renderer.setSize(window.innerWidth, window.innerHeight); // 创建辅助轴线
var axis = new THREE.AxisHelper(100);
scene.add(axis); // 添加图形---圆柱体
let _radius = 5;
var cylinderGeometry = new THREE.CylinderGeometry(_radius, _radius, 20, 40, 40);
var cylinderMaterial = new THREE.MeshBasicMaterial({ color: 0x765432 });
var cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial);
scene.add(cylinder); // 将图形输出到页面
document.getElementById('webgl-output').appendChild(renderer.domElement); // 逐帧绘制方法
function renderScene() {
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
} // 开启渲染
renderScene();
} // 画布自适应
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
} // 射线拾取
function initRay(event) { // 获取画布
let mainCanvas = document.querySelector("#webgl-output canvas"); // 将屏幕坐标转为标准设备坐标(支持画布非全屏的情况)
let x = ((event.clientX - mainCanvas.getBoundingClientRect().left) / mainCanvas.offsetWidth) * 2 - 1; // 设备横坐标
let y = -((event.clientY - mainCanvas.getBoundingClientRect().top) / mainCanvas.offsetHeight) * 2 + 1; // 设备纵坐标
let standardVector = new THREE.Vector3(x, y, 1); // 设备坐标 // 标准设备坐标转为世界坐标
let worldVector = standardVector.unproject(camera); // 射线投射方向单位向量(worldVector坐标减相机位置坐标)
let ray = worldVector.sub(camera.position).normalize(); // 绘制射线
drawRay(scene, camera.position, ray); // 创建射线投射器对象
let rayCaster = new THREE.Raycaster(camera.position, ray); // 返回射线选中的对象数组(第二个参数默认值是false,意为是否遍历图形内部的所有子图形)
let intersects = rayCaster.intersectObjects(scene.children, true);
if (intersects.length > 0) { // 射线拾取的首个对象
let currObj = intersects[0]; console.log(currObj); // 改变被拾取对象的材质颜色(随机)
let currcolor = `rgb(${Math.floor(Math.random() * 256).toString()},${Math.floor(Math.random() * 256).toString()},${Math.floor(Math.random() * 256).toString()})`
currObj.object.material.color.set(currcolor);
}
} // 绘制射线(箭头射线)
function drawRay(scene, start, dir) {
let prevRay = scene.getObjectByName("customRay");
if (prevRay) {
scene.remove(prevRay);
} let arrow = new THREE.ArrowHelper(dir, start, 1000, 0x0000ff);
arrow.name = "customRay";
scene.add(arrow);
} // 初始化
window.onload = init();
</script>
</body> </html>
参考原文地址: https://segmentfault.com/a/1190000010490845
threeJS射线拾取机制及案例的更多相关文章
- UE4 射线拾取&三维画线
虽然有人建议UE4使用C++创建VR项目,能避免一些坑爹的错误,但是我用C++创建,竟然问题更多,还存在创建不了的情况,也不知道是不是我的操作问题,快疯了. 于是我还是选择了蓝图创建VR项目,但是.. ...
- WebGL射线拾取模型——八叉树优化
经过前面2篇WebGL射线拾取模型的文章,相信大家对射线和模型面片相交的原理已经有所了解,那么今天我们再深入探究关于射线拾取的一个问题,那就是遍历场景中的所有与射线相交的模型的优化问题.首先我们来复习 ...
- 【转载】Java虚拟机类加载机制与案例分析
出处:https://blog.csdn.net/u013256816/article/details/50829596 https://blog.csdn.net/u013256816/articl ...
- three.js raycaster射线碰撞的坑 (当canvas大小 不是屏幕大小是解决拾取物体的办法)
这里只是记录一下坑,方便查阅,内容主要援引自:three.js Raycaster 射线拾取 canvas不占满整屏时射线拾取存在偏差 1. 世界坐标系: 世界坐标系位于屏幕的中心(0,0,0),往右 ...
- WebGL模型拾取——射线法二
这篇文章是对射线法raycaster的补充,上一篇文章主要讲的是raycaster射线法拾取模型的原理,而这篇文章着重讲使用射线法要注意的地方.首先我们来看下图. 我来解释一下上图中的originTr ...
- Modern OpenGL用Shader拾取VBO内单一图元的思路和实现
Modern OpenGL用Shader拾取VBO内单一图元的思路和实现 什么意思? 拾取 最简单的理解拾取的方式大概是到(http://www.yakergong.net/nehe/course/t ...
- ThreeJS中的点击与交互——Raycaster的用法
基础概念 坐标系 我们的手机屏幕是二维的,但是我们展示物体的世界是三维的,当我们在构建一个物体的时候我们是以一个三维世界既是世界坐标来构建,而转化为屏幕坐标展示在我们眼前,则需要经历多道矩阵变化,中间 ...
- ThreeJs 基础入门
本文来自网易云社区 作者:唐钊 Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它在 web 中创建各种三维场景,包括了摄影机.光影.材质等各种对象.使用它可以让我们更加直观的了解 we ...
- OpenGL 3D拾取文章(转)
参考文章 深入探索3D拾取技术 OpenGL 3D拾取 射线和三角形的相交检测(ray triangle intersection test) 3D拾取的方法有两种 1.基于几何计算的射线-三角形相交 ...
随机推荐
- 浅谈关于SQL优化的思路
零.为什么要优化 系统的吞吐量瓶颈往往出现在数据库的访问速度上 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢 数据是存放在磁盘上的,读写速度无法和内存相比 一.观察 MySQL优 ...
- Oracle查询中文乱码
1.查询Oracle服务端字符集 SQL> select userenv('language') from dual ; USERENV('LANGUAGE') ---------------- ...
- Linux实用技巧--隧道
平时开发过程中,可能会遇到一些网络问题,比如npm install 一些依赖包.本地电脑是可以,没有问题.但是测试环境服务器,由于公司内部网络安全限制,不可以随意访问外部网络.因此下载一个依赖包就变得 ...
- mariadb(三)查
-查询基本使用(条件,排序,聚合函数,分组,分页) 1)创建一个表结构然后添加数据 create table baba (id int unsigned not null auto_increment ...
- Installing Symfony project with PHP 7.3 version
参考地址:https://cmsdk.com/php/installing-symfony-project-with-php-7-3-version.html Tryng to install (an ...
- 第 12 章 python并发编程之协程
一.引子 主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只用一个)情况下实现并发,并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作 ...
- Git使用gitignore建立项目过滤规则
在进行协作开发代码管理的过程中,常常会遇到某些临时文件.配置文件.或者生成文件等,这些文件由于不同的开发端会不一样,如果使用git add . 将所有文件纳入git库中,那么会出现频繁的改动和push ...
- python实现获取文件的绝对路径
实现代码如下: #获取文件的绝对路径import osclass GetPath: def get_path(self,path): r=os.path.abspath(path) return ri ...
- 题解[SCOI2009]粉刷匠 难度:省选/NOI-
Description windy有 N 条木板需要被粉刷.每条木板被分为 M 个格子.每个格子要被刷成红色或蓝色.windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色.每个格子最多 ...
- CentOS7通过SpeedTest工具测速
首先要安装SpeedTest工具,这里提供两种方法安装SpeedTest: 一.通过直接下载SpeedTest脚本,给权限运行脚本即可 [root@bogon ~]#wget -O speedtest ...