WebGL初探—Three.js全景图实战

JavaScript020

WebGL初探—Three.js全景图实战,第1张

前段时间公司给了一个新需求就是写一个装修室内3D全景效果图,于是开始我的three.js开发之旅。

作为一个前端小白,突然接触three.js&webgl除了懵逼还是懵逼,不过作为一个技术人对于挑战也许就是软件开发中真正的乐趣,至少不会埋头调试一遍又一遍重复的页面数据,上上下下左左右右BABA......简直枯燥到极点。不过three.js&webgl不得不说给我打开了新的世界,接下来我就简单讲述一下我的学习之旅。

Three.js 是一款运行在浏览器中的 3D 引擎,是JavaScript编写的WebGL第三方库,可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象,three.js内部也是webgl的封装,封装了大量了webgl API ,让比较繁琐的webgl更加简便。

WebGL(全写Web Graphics Library)是一种3D绘图协议,它让可以让开发进一步去了解图形渲染,Webgl是JavaScript和OpenGL ES 2.0合并出来的升级版,通过webgl可以让前端开发者们脱离开css渲染,可以了解更加底层的渲染,WebGL也可以为HTML5 Canvas提供硬件3D加速渲染,webgl是通过系统显卡来在浏览器里更流畅地展示3D场景和模型,加入shader(着色器)来对图形渲染,学习webgl需要具备相应的图形学算法,属于目前图形渲染开发的高级技术之一。目前webgl也运用在游戏,视频特效,包含untiy3D也是集成webgl。

three.js中主要由摄像机 ,场景 ,渲染器 , 资源加载器,素材组成

webgl中的所有东西都是基于摄像机去展示的,可以利用摄像头的视角形成对3d视图观测视角,比如鱼眼视角,从而就让我们可以在平面图上可以开发出真实场景的3D视图。接下来我们看看怎么用three.js创建一个摄像机:

摄像机有了但是为了让景物可以更好的展现,这时候我们就需要一个展示景物的场景,three.js也为我们封装好了,如下所示可以创建一个场景:

渲染器是webgl的渲染启动开关,他可以调用render方式把场景渲染到摄像机。

three.js加载资源不同我们常见的html一样,直接通过src属性加载,而是通过TextureLoader.load来加载资源。

素材常见的包含网格,灯光等许多元素下面我就举个例子

http://www.sunql.top/webgldemo/index.html

https://github.com/sunql0827/webgldemo.git

https://gitee.com/sunql-hugh/webgldemo.git

通过这次基于three.js的webgl全景图开发之旅为我对视图渲染打开了一道新的大门,不过webgl的厉害之处还有很多很多是我还未涉及到了,以后还需要更加努力了。

可能很多同学会发现,学习Three.js的API非常容易,但是真正理解API的作用却非常难。其实让大家感到难的并不是Three.js本身,而是Three.js背后所隐藏的3D图形学知识。本系列Three.js源码解读文章,会帮你一边补齐3D图形学的基础知识,一边真正理解到Three.js的实现原理,知其然,知其所以然。

Object3D是ThreeJS中大部分物体的基类,它包含了物体的位移,旋转,缩放,以及各个物体父子关系的js实现。选取Object3D几个重要的属性做解释:

一个3D对象往往由多个父子对象组成,父对象的位移, 旋转, 缩放会传递给所有的子对象。

this.parent指向父对象,this.children包含了所有的子对象。

通过 add 为物体添加子对象。需要注意的是,如果该子对象有其他的父对象,会先解除子对象和旧的父对象的父子关系,然后将子对象添加到新的父对象中。

this.matrix表示物体自身的本地形变,this.matrixWorld表示物体的全局形变。当物体没有父对象时,全局形变就是本地形变。

为什么对象组合这么重要呢?看下面的例子:

这两个立方体共同组成了一个3D对象,下面的立方体为底座,上面的立方体为操作臂。当底座转动的时候,操作臂会同样转动,所以操作臂的形变会传递给底座。当操作臂旋转时,底座不会被影响。

这里,底座就是操作臂的父对象。只要简单的将底座的全局形变(this.parent.matrixWorld)和操作臂的本地形变(this.matrix)相乘,就能得到操作臂的最终形变。是不是很方便?

3D物体的位移,旋转,缩放都可以通过矩阵表示。其中,旋转除了通过矩阵,还可以通过欧拉角和四元数表示。

Object3D的rotation代表物体旋转的欧拉角表示,quaternion代表了四元数表示,他们是3D物体统一旋转的不同数学表达方式。(矩阵,欧拉角,四元数表示旋转

onRotationChange , onQuaternionChange 这两个回调用于同步欧拉角和四元数,保证他们代表着相同的旋转角度。

3D交互一个很大一部分工作量是需要在物体的本地空间( this.matrix )和世界空间( this.matrixWorld )进行坐标转换。

1. 需要有一个被选择的mesh,通过raycaster获取 2. 创建先和一个平面 3. 将线和标注的面放在获取的mesh上 var mouse =new THREE.Vector2() var raycaster =new THREE.Raycaster() mouse.x = (event.clientX / domElement.width) *2 -1 mouse.y = -(event.clientY / domElement.height) *2 +1 raycaster.setFromCamera(mouse, camera) var intersects = raycaster.intersectObjects(scene.children, false)//  selectedObject = intersects[0].object// 被选中的标注对象 //  var startP,endP// 这里也是通过raycaster获取到的 var color = parameters.hasOwnProperty("color") ? parameters.color :0x0000ff  var lineMat =new THREE.LineBasicMaterial({         "color": 0xff0000 }) var lineGeo =new THREE.Geometry() lineGeo.vertices.push(startPos) lineGeo.vertices.push(endPos) var line =new THREE.Line(lineGeo, lineMat) // 初始化一个sprite,这是一个始终面向相机的平面, var sprite = new textSprite(这并不是一个构造函数,不想写那么多,自行去官网查看) ....... sprite.scale.set(_spriteScale, _spriteScale, _spriteScale)// 设置的是sprite的大小 selectedObject.add(line)// 添加 line.worldToLocal(_endPos)// 设置点 sprite.position.copy(_endPos)// 设置sprite的位置 line.add(sprite)添加并进行关联 注: sprite可以用于进行标注热点等操作,具体实现较为简单,同时因为也算是mesh,所以sprite可以设置texture,使用图片作为背景等,也可以把一个canvas,把需要的文字图片等都加入进来.