基于Three.js的拾取方法的研究

2021-07-20 12:02余莉
计算机时代 2021年6期

余莉

摘  要: Three.js是一个由JavaScript编写的WebGL框架,可以调用API直接在浏览器中创建和渲染三维场景。与虚拟场景进行交互的最常见方式就是鼠标拾取,点击对象可显示所点击物体信息,或修改物体属性,或激活动画。文章先介绍了拾取的基本原理,再分析和实现了组合模型的拾取、外部导入模型的拾取以及拾取多个对象等三个方面的特殊应用,对THREE.Raycaster对象的用法进行了详尽的介绍,能较全面地满足拾取在教学和虚拟现实中的应用。

关键词: Three.js; 拾取; THREE.Raycaster对象

中图分类号:TP37          文献标识码:A     文章编号:1006-8228(2020)06-61-04

Abstract: Three.js is a WebGL framework written with JavaScript, which can call APIs to create and render 3D scene directly in browser. The most common way to interact with the virtual scene is to pick up by the mouse. Click on object can display the information of the selected object, modify the object attributes, or activate animation. The basic principle of pick-up is introduced in this paper, and three special applications are analyzed and realized, i.e. the pick-up of group model, pick-up of external imported model and pick-up multiple objects, the usage of THREE.Raycaster object is also introduced in detail, which can meet the application of pick-up in teaching and virtual reality more comprehensively.

Key words: Three.js; pick-up; THREE.Raycaster object

0 引言

隨着前端展示技术的发展,HTML5和WebGL技术在虚拟现实领域的应用正逐渐增多。由于浏览器原生支持WebGL,所以不需要安装任何插件,即可观看3D效果,且具有很好的跨平台性。电脑、平板、手机等终端,只要安装有支持HTML5的浏览器,即可使用。但是直接使用WebGL编程是十分复杂的,需要扎实的计算机图形学基础,学习复杂的着色器语法[1]。Three.js库是一个由JavaScript编写的WebGL框架,实现了WebGL的封装与扩展[2],降低了初学者开发的难度。Three.js库及其加载器可以很方便地把JSON、OBJ等格式的三维模型加载到网页中来展示,并进行光照、材质、纹理计算后的实时渲染,用户可使用鼠标旋转、平移和缩放,与虚拟场景进行简单交互[3]。

1 拾取的基本原理

浏览器上最常见的交互就是通过鼠标点击三维物体,检测实物图形是否位于鼠标指针的下方[4],即物体的拾取操作。选中的对象会改变颜色、可见性等属性,如图1所示,选中的立方体被改为红色,或者激活对象的动画。

拾取的过程恰好与实物图形绘制的过程相反。由于鼠标点击对应的是浏览器的屏幕坐标(X,Y),而three.js画布是三维场景,所以需要将屏幕点击的二维坐标转换为Three.js的三维坐标(X',Y'),以确定点击的对象模型,如图2所示,实线坐标系为二维屏幕坐标系,左上角为原点;虚线坐标系为三维世界坐标系,中心点为原点。屏幕坐标转换成三维场景坐标代码如下:

var vector=new THREE.Vector3((event.clientX/

window.innerWidth)*2-1,

-(event.clientY/window.innerHeight)*2+1,0.5);

vector=vector.unproject(camera);

坐标拾取的方法涉及复杂的矩阵运算[5],好在Three.js类库提供了THREE.Raycaster对象用于鼠标拾取,这是一个传统的面向对象空间的拾取方法[6]。其构造函数为new Raycaster(origin,direction,near,far);其中origin是光线投射的起点向量,就是camera的位置;direction是光线投射的方向向量(被归一化)。常用方法为.intersectObject(object,recursive);其中object是参与相交检测的模型集合,返回值为相交模型的集合,该数组按距离排序,最接近视点的排在第一个。求交的伪代码如下:

var raycaster=new THREE.Raycaster(camera.position,

vector.sub(camera.position).normalize());

var intersects=raycaster.intersectObjects

([参与相交检测的模型集合]);

if (intersects.length > 0) {

//intersects[0].object为最近的拾取对象

}

//else没有对象被拾取到

2 特殊的拾取

2.1 组合模型的拾取

⑴ 拾取组合模型的成员

可以通过Three.js自带的功能来组合或合并已有的几何体,创建出新的几何体[7]。如图3(a)所示的小兔子,就是通过基本的cube、sphere组合成的兔子,可以做局部(耳朵、胳膊、腿等)以及整体的动画。例如通过点击兔子的耳朵,让耳朵变长,如图3(b)所示。设计代码如下:

var intersects=raycaster.intersectObjects(rabbit.children);

if (intersects.length>0) {

intersects[0].object.flag=!intersects[0].object.flag;

//单击一次true,再一次false

}

然后在requestAnimationFrame(render);回调的render()函数中添加if(rabbit.ear1.flag)rabbit.ear1.scale.y*=1.01;语句。

⑵ 拾取组合模型组

但是,使用raycaster射线无法获取组group,只能获取组中的元素。如果我们希望点击兔子,就能让兔子旋转,那么必须是点击兔子的头、身体或者四肢,即参与相交检测的模型集合必须是rabbit.children,rabbit本身并不能被拾取到,如图3(c)所示。伪代码为:

var intersects=raycaster.intersectObjects(rabbit.children);

if (intersects.length>0) {

if (intersects[0].object.parent==rabbit)

//如果点击兔子的children

rabbit.flag=!rabbit.flag; //兔子被选中或者取消选中

else

//如果有其他模型被选中时的代码

}

然后在render()函数中添加if(rabbit.flag==rabbit) rabbit.rotation.y+=0.02;语句。

2.2 外部导入模型的拾取

使用基本几何体创建模型的工作量较大,通过各种加载器,Three.js可以加载JSON、OBJ、STL等多种格式的模型,降低了模型制作的成本。如图4(a)中房子的模型是用3DMAX制作的,图4(b)中火箭是用MAYA制作的。在google chrome浏览器的“开发者工具”里,可以使用console.log(rocket.children);来查看火箭模型的成员,如图4(c)所示,该火箭是由15个部分组成的。图4(a)中的小房子是只有一个成员的数组,见图4(d)。外部导入模型的拾取方式伪代码为:

var intersects=raycaster.intersectObjects([其他拾取模型]

.concat(rocket.children));

if (intersects.length>0) {

if (intersects[0].object.parent==rocket)

//如果点击火箭的children

rocketflag=!rocketflag; //rocketflag是个全局变量

else

//如果有其他模型被選中时的代码

}

然后在render()函数中添加if (rocketflag) rocket.position.y+=0.02;语句,火箭就会升空。

2.3 拾取多个对象

通常情况下,拾取是选中离视点最近的那一个三维模型。但某些特殊情况下,用户希望选中raycaster射线上所有的对象。例如在模拟一个机关枪的扫射效果时,需要选中射程上的所有对象,如图5(a)中用鼠标表明鼠标拾取位置,并用线框标识射程上的两个对象;图5(b)是这两个对象不可见后的效果。

代码如下:

var intersects=raycaster.intersectObjects(scene.children,true);

//第二个参数true表明对象的所有后代也参与求交,默认值为false

for (var i=0; i

intersects[i].object.visible=false;

//相交集合中所有元素都不可见

}

3 小结

本文针对于网页中常见的拾取方式进行设计和实现,阐释了传统的基于对象空间的拾取方法的基本原理,并分别实现了对组合模型的拾取、外部导入模型的拾取和拾取多个对象等三方面的特殊应用,能够很好地满足教学和虚拟现实中拾取的需求,因此该研究具有一定的应用价值。下一步的工作可考虑虚拟场景漫游中碰撞检测的快速实现,以及提高实时绘制性能的LOD技术的应用等。

参考文献(References):

[1] 张文娟,吴琼,曹欣然.基于WebGL的三维落叶场景仿真[J].计算机技术与发展,2018.28(6):171-175

[2] 赵海鹏,周杨,卞和方.基于Three.js的三维虚拟校园系统设计与实现[J].兰州交通大学学报,2019.3.

[3] 汪浩,田丰,张文俊.基于WebGL的交互平台设计与实现[J].电子测量技术,2015.8:119-122

[4] 宁静.基于WebGL实物交互技术及其实现的研究[D].华中科技大学,2014.

[5] 施珂奕,邓春健,邹昆.基于OpenGL的三维模型点坐标拾取方法[J].液晶与显示,2016.31(7):708-713

[6] 邹建达,原力,毛力奋.基于Three.js的在仿真中的可视化和拾取研究[J].电脑与信息技术,2019.5.

[7] Jos Dirksen.Three.js开发指南:WebGL的JavaScript 3D库[M].机械工业出版社,2017.