3D可视化库-Threejs调研及简单示例

0 背景

WebGL是一种3D绘图协议,其允许JavaScript和OpenGL ES2.0结合在一起,为H5 Canvas提供硬件3D加速渲染,可以借助系统显卡在浏览器里更流畅地显示3D场景和模型。Threejs是一款webGL框架,由于其易用性被广泛应用。Threejs在WebGL的api接口基础上,又进行了一层封装。

WebGL原生的api是一种非常低层的接口,需要一些数学和图形学的相关技术。其解决是如何在画布上画图的问题,怎么画点、线、面,怎么上色,怎么贴图,怎么处理光线,视角转动之后怎么换算绘制等等。对于没有相关基础的人来说,入门很难,Three.js将入门的门槛降低了一大截,其解决底层的渲染细节和复杂的数据结构,将复杂的底层细节抽象出来,简化我们创建三维动画场景的过程。

1 Threejs核心概念

为快速入手,在使用threejs之前,需要了解场景、照相机、对象、光、渲染器等核心概念。

1.1 场景-Scene

场景是所有物体的容器,对应着现实生活中三维世界,所有的可视化对象及相关的动作均发生在场景中。

1.2照相机-Camera

Camera是三维世界中观察者,类似与眼睛。为了观察这个世界,需要描述空间中的位置,three.js采用右手坐标系。

                                             3D可视化库-Threejs调研及简单示例_第1张图片

 

Threejs中的Camera有两种,分别是正交投影相机THREE.OrthographicCamera和透视投影相机THREE.PerspectiveCamera。

                                                 3D可视化库-Threejs调研及简单示例_第2张图片

 

 

正交投影与透视投影的区别如上图所示,左图是正交投影,物体发出的光平行地投射到屏幕上,远近的方块都是一样大的;右图是透视投影,近大远小,符合我们平时看东西的感觉。

1.3 对象-Objects

对象则是场景中被观察的物体,Threejs中供显示的物体有很多,它们都继承自Object3D类,主要的对象有两种Mesh和Points。

我们都知道,计算机的世界里,一条弧线是由有限个点构成的有限条线段连接得到的。线段很多时,看起来就是一条平滑的弧线了。计算机中的三维模型也是类似的,普遍的做法是用三角形组成的网格来描述,我们把这种模型称之为Mesh模型。 
                                                                 3D可视化库-Threejs调研及简单示例_第3张图片
 

这是著名的斯坦福兔子,随着三角形数量的增加,它的表面越来越平滑准确。

在Three中,Mesh的构造函数是这样的:Mesh( geometry, material )。geometry是它的形状,material是它的材质。不止是Mesh,创建很多物体都要用到这两个属性。下面我们来看看这两个重要的属性。

Geometry--形状,它通过存储模型用到的点集和点间关系(哪些点构成一个三角形)来达到描述物体形状的目的。Three提供了立方体(其实是长方体)、平面(其实是长方形)、球体、圆形、圆柱、圆台等6种基本形状;你也可以通过自己定义每个点的位置来构造形状;对于比较复杂的形状,我们还可以通过外部的模型文件导入。

Material--材质,材质其实是物体表面除了形状以为所有可视属性的集合,例如色彩、纹理、光滑度、透明度、反射率、折射率、发光度。Threejs里需要知道材质(Material)、贴图(Map)和纹理(Texture)的关系。材质包括了贴图以及其它。贴图其实是‘贴’和‘图’,它包括了图片和图片应当贴到什么位置。纹理其实就是‘图’。对于复杂的材质,可以通过Threejs提供的贴图和纹理api实现。同时,Threejs提供了多种材质可供选择,能够自由地选择漫反射/镜面反射等材质。
        Points是另一种对象,其实就是一堆点的集合,它在之前很长时间都被称为ParticleSystem(粒子系统),而Three中的Points简单得多。因此最终这个类被命名为Points。 

1.4 Light-光

 同现实世界一样,我们要看到物体需要光,光影效果是让画面丰富的重要因素。Three提供了包括环境光AmbientLight、点光源PointLight、 聚光灯SpotLight、方向光DirectionalLight、半球光HemisphereLight等多种光源。只要在场景中添加需要的光源,即可实现相应得光效果。

1.5 Renderer-渲染器

在场景中建立了各种物体,也有了光,还有观察物体的相机,Renderer则负责将物体渲染到场景中。Renderer绑定一个canvas对象,并可以设置大小,默认背景颜色等属性。 
调用Renderer的render函数,传入scene和camera,就可以把图像渲染到canvas中了。

2 举例-画一个简易草坪

2.1 初始化场景和相机

通过new THREE.Scene()实例化一个场景,new THREE.PerspectiveCamera()实例化透视相机,并设置相机位置和观察方向。

        //初始化场景

        this.scene = new THREE.Scene()

         //初始化相机

        this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 5000)

        this.camera.position.set(330, 330, 330)

        this.camera.lookAt(this.scene.position)

2.2 初始化渲染器

new THREE.WebGLRenderer()实例化渲染器,参数canvas为绑定的标签为canvas的dom元素。

        //初始化渲染器,绑定导canvas的dom元素上

        this.renderer = new THREE.WebGLRenderer({ 

            antialias: true,

            canvas: document.querySelector('canvas')

        });

        //设置渲染器的大小及背景设置

        this.renderer.setSize(this.width, this.height)

        this.renderer.setClearColor(this.config.background)

        this.renderer.shadowMap.enabled = true //是否有阴影

        this.renderer.shadowMap.type = THREE.PCFSoftShadowMap

2.3构建光系统

  buildLightSystem()函数实现平行光的创建及添加到初始化的场景中,包括平行光的位置,及照射方向等。

buildLightSystem() {

         //平行光

         let directionalLight = new THREE.DirectionalLight(0xffffff, 1.1);

         directionalLight.position.set(300, 1000, 500);

         directionalLight.target.position.set(0, 0, 0);

         directionalLight.castShadow = true;

 

         let d = 300;

       

        directionalLight.shadow.camera = new THREE.OrthographicCamera(-d, d, d, -d, 500, 1600)

        directionalLight.shadow.bias = 0.0001;

        directionalLight.shadow.mapSize.width = directionalLight.shadow.mapSize.height = 1024;

        //将光添加到场景中

        this.scene.add(directionalLight)

 },

 

2.4 绘制模型对象

Buildbuilding()函数实现场景中核心对象的创建及添加场景功能。addPlane()创建地面,addFense()创建围墙。

addPlane()函数中new THREE.BoxBufferGeometry(320,6,320)创建x=320,y=6,z=320的长方体,utils.makeMesh()工具类为该长方体对象增加表面材质。plane.positon.y设置长方体对象在场景中y轴的位置。this.scene.add(plane)增加该长方体面板到场景中。

  Buildbuilding(){

     //添加地板

     this.addPlane();

     //添加围墙

     this.addFense();

},   

function addPlane(){

      let planeGeometry = new THREE.BoxBufferGeometry(320, 6, 320)

      let plane = utils.makeMesh('lambert', planeGeometry, 0x6f5f6a)

      plane.position.y = -3

      this.scene.add(plane)

}

                                         3D可视化库-Threejs调研及简单示例_第4张图片

addFense()函数实现不规则3维围墙,fenseCoords定义了二维围墙各个拐点的坐标集合,makeShape方法通过拐点集合划线,实现由点到面的二维不规则平面。makeExtrudeGeometry方法将二维图形拉伸为3为图形,拉伸高度为第二个参数所决定。同样由makeMesh函数实现物体材质的实现并最后添加到场景中。

            function addFense() {

                //12条边,12个点,首尾点相同,转一圈。

                let fenseCoords = [

                    [-130, -130],

                    [-130, 130],

                    [130, 130],

                    [130, -130],

                    [20, -130],

                    [20, -120],

                    [120, -120],

                    [120, 120],

                    [-120, 120],

                    [-120, -120],

                    [-20, -120],

                    [-20, -130],

                    [-130, -130]

                ]

                let fenseShape = utils.makeShape(fenseCoords)

                //将二维图形拉伸为三维图形,高度为3

                let fenseGeometry = utils.makeExtrudeGeometry(fenseShape, 3)

                let fense = utils.makeMesh('lambert', fenseGeometry, 0xe5cabf)

                _this.scene.add(fense)

            }

                                       3D可视化库-Threejs调研及简单示例_第5张图片

2.5 页面初始化挂载执行

浏览器dom渲染时,依次执行上述函数,实现初始化场景、相机、渲染器、光系统、物体构建及挂载渲染器上,实现整个三维物体的可视化。

 mounted() {

//初始化场景

        this.scene = new THREE.Scene()

         //初始化相机

        this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 5000)

        this.camera.position.set(330, 330, 330)

        this.camera.lookAt(this.scene.position)

//初始化渲染器,绑定导canvas的dom元素上

        this.renderer = new THREE.WebGLRenderer({ 

            antialias: true,

            canvas: document.querySelector('canvas')

        });

        this.renderer.setSize(this.width, this.height)

        this.renderer.setClearColor(this.config.background)

        this.renderer.shadowMap.enabled = true //是否有阴影

        this.renderer.shadowMap.type = THREE.PCFSoftShadowMap

        //构建光系统

        this.buildLightSystem()

        //构建建筑

        this.buildbuilding()

          //场景、相机挂载到渲染器上

this.renderer.render(this.scene, this.camera)

}

3 总结

  1. 构建一个三维空间,Three中称之为场景(Scene)
  2. 选择一个观察点,并确定观察方向/角度等(Camera)
  3. 构建光系统,用于观察物体(light)
  4. 在场景中添加供观察的物体 ,Three中的物体有很多种,包括Mesh,Line,Points等,它们都继承自Object3D类
  5. 将观察到的场景渲染到屏幕上的指定区域 ;Three中使用Renderer完成这一工
  6. 辅助开源库,dat.GUI是一个一个轻量级的图形用户界面库,或者说GUI组件,只有几十KB,可以用于创建操作控制三维场景的菜单栏;stats.min用来进行性能监控的插件;OrbitControls辅助进行旋转缩放拖动等控制;
  7. 对于物体的建模,简单的物体可以通过Three提供的api编程实现,对于复杂的物体可以借助建模工具先建模,然后引入到程序中,实现物体的渲染和控制。

 

你可能感兴趣的:(WEBGL,javascript)