前端图形入门在WebGL中实现3D物体的选中和操作
最近一年一直在做3D图形相关的应用,入坑前端图形也了一年,想在这里跟大家分享一下自己学习到一些图形基础知识。今天跟大家分享一下在Web中实现3D物体的选中和移动旋转缩放操作。
我们先看一下实现的最终效果:
实现思路初始化3D场景加载3D模型处理mousemove事件和click事件模型选中样式更新菜单显示菜单操作实现实现说明初始化3D场景
ThreeJS中的一些基本概念:场景、相机、灯光、渲染器、3D物体Scene(场景):一个容器,用于放置各种物体:物体、灯等等Camera(相机):模拟人眼观察,需要放置到场景中Light(灯光):模拟各类灯光类型Renderer(渲染器):用于渲染所有信息Mesh(3D网格物体):Mesh信息
首先我们需要在ThreeJS中初始化场景创建一个场景this。scenenewTHREE。Scene();this。scene。backgroundnewTHREE。Color(0xbbbbbb);创建渲染器this。renderernewTHREE。WebGLRenderer({canvas:this。canvas});const{width,height}this。domContainer。getBoundingClientRect();this。renderer。setSize(width,height);this。renderer。setPixelRatio(window。devicePixelRatio);相机this。cameranewTHREE。PerspectiveCamera(75,window。innerWidthwindow。innerHeight,0。1,1000);this。camera。position。set(80,60,0);this。camera。lookAt(0,0,0);加载3D模型
ThreeJS中可以使用API生成3D物体,也可以通过Loader加载其他3D格式的模型,如常见的obj模型或者其他glTF格式的模型数据,创建本地3D模型constgeometrynewTHREE。BoxGeometry(10,10,10);constmaterialnewTHREE。MeshBasicMaterial({color:0xc20053});material。sideTHREE。DoubleSide;constmeshnewTHREE。Mesh(geometry,material);加载第三方3D模型使用GLTFLoader加载模型constloadernewGLTFLoader()。setPath(assets);loader。load(Soldier。glb,(gltf:GLTF){this。scene。add(gltf。scene);});添加灯光
在真实的世界中,我们能看到物体是因为眼睛接收到物体表面反射光,形成的图像,在我们这里使用HemisphereLight模拟一个简单的户外光照效果consthemiLightnewTHREE。HemisphereLight(0xffffff,0x444444);hemiLight。position。set(0,20,0);this。scene。add(hemiLight);渲染
渲染器、场景、物体、相机和灯光都准备好后,我们就可以在浏览器中渲染出这个模型privateanimationFrame(){window。requestAnimationFrame(this。animationFrame);if(this。application。renderNextFrame){this。renderer。render(this。scene,this。camera);}};帧循环渲染window。requestAnimationFrame(this。animationFrame);
提问:requestAnimationFrame属于微任务还是宏任务(字节面试题)?处理mousemove事件和click事件
经过上面的操作,我们已经可以在canvas中显示3D场景和物体了
那我们要实现在不同角度对3D物体的选中,就需要在canvas中监听鼠标事件,根据鼠标的位置查找选中的物体信息
在3D中实现对物体的选中,就是在鼠标所在的位置发出一条摄像,和场景中的物体做相交,ThreeJS中可以直接使用Raycasterthis。mousePoint。xevent。clientXthis。domContainer。clientWidth21;this。mousePoint。y(event。clientYthis。domContainer。clientHeight)21;this。raycaster。setFromCamera(this。mousePoint,this。camera);this。meshes包含了所有物体信息constintersectsthis。raycaster。intersectObjects(this。meshes);
这里setFromCamera就是在当前鼠标位置和相机设置设定射线发射器,raycaster。intersectObjects来做射线和指定的物体相交,返回检测到的物体列表
需要注意的是,ThreeJS中,一个物体可能由N级单元构成,而射线只能检测到最小单元,在返回的intersects中,我们需要去找到对应的物体,所以在添加物体到场景中时,我们需要建立一个单元物体的映射gltf。scene是加载出来的一个机器人整体gltf。scene。traverse((object:THREE。Object3D){if(objectinstanceofTHREE。Object3D){meshes存储所有物体单元信息this。meshes。push(object);存储单元机器人的映射this。objects。set(object。uuid,gltf。scene);}});
根据raycaster。intersectObjects返回的信息,我们可以拿到当前鼠标选中的物体if(intersects。length0){选中的最小单元constmeshintersects〔0〕。object;找到对应的物体constpickedthis。objects。get(mesh。uuid);}模型选中样式更新
这个时候我们已经拿到了选中的物体,那么就是要给个反馈呗,这里我们做个简单的样式更改,把选中的物体材质透明度改为原始值的一半,那么在下一帧刷新后,我们就可以看到一个透明的效果mesh。traverse((child:THREE。Object3D){if(childinstanceofTHREE。Mesh){constmaterialchild。materialasTHREE。Material;存储之前的透明度,鼠标移出后记得把材质修改回来this。meshOpacity。set(child。uuid,material。opacity);设置透明度material。opacitymaterial。opacity3;material。transparenttrue;material。needsUpdatetrue;}});菜单显示
经过以上步骤后,我们就了解了在ThreeJS中如何去选中物体,那一般我们的业务需要选中后,有一些操作,就比如游戏中的走路,这里我们就来看看ThreeJS中最常见的三种变换:移动、旋转、缩放
当我们在3D中选中物体后,告诉我们的菜单,这个时候你该出场了,我项目中用了一个简单的发布订阅者模式,当物体选中后,会向系统发出物体选中事件constselectionsobjects。map((mesh)newJ3DSelection(mesh));this。application。addSelections(selections);this。application。emitSelectionAdd({selections,event});
而在菜单功能那边,会监听选中事件this。application。listenSelectionAdd(this。onSelectionAdded);
从而在收到物体选中事件后,把自己show出来privateupdateMenu(event:MouseEvent){const{clientX,clientY}event;this。setState({xPosition:clientX10,yPosition:clientY10,visible:this。application。selections。length0});};
效果如下:
菜单操作实现
菜单展示出来后,点击对应的按钮,根据选中的物体信息执行相应的操作即可移动privatemove(direction:number){constdelta:numberdirection0?5:5;this。application。selections。forEach((item){item。mesh。position。zitem。mesh。position。zdelta;});};旋转privaterotate(direction:number){constangledirection0?Math。PI4:Math。PI4;this。application。selections。forEach((item){item。mesh。rotateY(angle);});};缩放privatescale(direction:number){constdelta:numberdirection0?2:0。5;this。application。selections。forEach((item){const{x,y,z}item。mesh。scale;item。mesh。scale。set(xdelta,ydelta,zdelta);});};总结
第一次写技术博客,主要是用一个直观的项目展示基于ThreeJS的3D基础知识,后续还会持续分享一些自己的前端和图形相关的知识,如有错误欢迎留言指正。项目源码
Github地址:GitHubjaliyj3dpick:在WebGL中实现3D物体的选中和操作(ThreeJS)
官方文档ThreeJS:基于WebGL封装的一个3DJS库,关于ThreeJS,可以看ThreeJS官网Three。jsJavaScript3DLibrary
WebGL:基于OpenGLES规范的Web端3D图形接口规范,更多知识可以看WebGL官网WebGLOverviewTheKhronosGroupInc
OpenGL:用于访问图形硬件(GPU)的接口标准,OpenGL官网OpenGLOverviewTheKhronosGroupInc
明察秋毫造句33句明察秋毫造句1、这件案子亏得法官明察秋毫,终於真相大白,洗刷了被告的冤情。2、你真是明察秋毫,问题的原因这麽快就找出来了。3、一位本来明察秋毫的人也会被头脑发……
语文修改病句专题、我党在幼年时期,我们对于马克思列宁主义的认识和对于中国革命的认识是何等肤浅,何等贫乏,则现在我们对于这些的认识是丰富得多,深刻得多了。、没有调查就没有发言权,侃侃而谈地……
微信红包封面开放平台即将升级个人也可以定制感谢IT之家网友MartinTan的线索投递!IT之家11月4日消息微信红包封面公众号今天发布消息称,微信红包封面开放平台也将进一步升级,为个人创作者赋能。具体如下……
雷军成立金山奇文企业管理咨询公司,持股100IT之家6月15日消息企查查App显示,6月9日,北京金山奇文企业管理咨询有限责任公司成立,法定代表人为雷军,注册资本10万元人民币,经营范围包含:企业管理咨询;软件开发;技术……
全国百大打车难地点出炉天津济南数量最多高德地图与中国社会科学院社会学研究所联合发布了《2019年度打车难分析报告》(以下简称《报告》),在全国20个主要城市综合研判出100个打车难地点,这些地点主要集中在华北和华东……
不必等到盛春再开花的阅读及答案不必等到盛春再开花你知道北方的苹果运往南方,在果树上什么时候采摘吗?你知道既要灭菌又要保持香味,葡萄酒要加温到多少度吗?那年我到山东烟台一处苹果园,看到果农将没成熟……
雷军为高考学子打call考的都会,会的都对IT之家7月6日消息小米集团创始人雷军发视频为高考学子送祝福,雷军说,33年前,我和大家一样怀着兴奋又有点忐忑的心情,踏入高考考场。当时我给自己鼓劲,放轻松、考的都会,会的都对……
刷子李教学反思范文【篇一:刷子李教学反思】《刷子李》是一篇略读课文,课文以刷子李的高超手艺为话题。既为奇人,则轶事多多,但作者只选择一件小事来写,借一件极富戏剧性的小事窥见人物的大本领、大……
SpaceX载人龙飞船定于8月2日返回地球,需630个小时北京时间7月28日晚间消息,SpaceX载人龙飞船进入轨道已近两个月时间,如今美国宇航局(NASA)计划让龙飞船于当地时间8月2日返回地球。当地时间5月30日(北京时间5……
广电总局对注水剧翻拍剧保持高压IT之家7月10日消息根据广电总局的消息,7月3日,聂辰席同志到广电总局电视剧司开展不忘初心、牢记使命主题调研。官方指出,针对注水剧、宫斗剧、翻拍剧、演员高片酬等问题,深入挖掘……
三年级美术留住秋天的教学反思范文今天上了三年级上册《留住秋天》,秋天是一个色彩缤纷的季节,丰收的季节,而伴随着这节课我也收获着,来和大家分享一下。真正精彩的课堂要呈现出精彩的学生。要学生学得精彩,就需要……
提高学生学习物理兴趣的几点尝试组织和设计课堂讲授内容和实验内容,充分发挥实验在教学中的直观性,让学生通过实验直接感知印象深刻。物理实验演示力求趣味性和新奇性,激发学生的好奇心理,从而激发他们思索的欲望……