Web3D - ThreeJS人物模型动画

2,177 阅读9分钟

目录:

ThreeJS概念WebGL兼容性开发准备 ThreeJS实战 Demo(捏脸、换装、动画、场景切换)建模工具Maya、3dmax、unity、blender => gltf/dae/fbx文件格式

ThreeJS 源码

THREEJS 性能优化

早期的canvas是一个H5标签,作用是在网页上画图,但是只支持2D,不支持3D。WebGL是一种 3D绘图标准,WebGL支持3D,且性能优于canvas。所以现在能用WebGL的都用WebGL,有些项目用canvas是因为部分手机不支持WebGL。

WebGL:允许工程师使用JS去调用部分封装过的 OpenGL ES2.0标准接口去 提供硬件级别的3D图形加速功能,底层通过WebGLRenderer把相机视椎体中的三维场景渲染成一个二维图片显示在画布上。

三者的关系是 JavaScript -> WebGL -> OpenGL ->.... -> 显卡 并把最终渲染出来图形 呈现到Canvas

ThreeJS概念

 Three.js是一个用javascript封装好的WebGL库,可以简单粗暴理解为WebGL方面的 jQuery,用于使用 WebGL 在 Web 浏览器中创建和动画化 3D 计算机图形。

四大组件

场景(scene)、相机(camera)和渲染器(renderer)、物体。

Threejs的基本要素包括以下几个方面:

场景:是一个三维空间,所有物品的容器。可以把场景想象成一个空房间,接下来我们会往房间里面放要呈现的物体、相机、光源。

相机:相机用来确定观察位置、方向、角度,相机看到的内容。在程序运行过程中,可以调整相机的位置、方向、角度。

:假如没有光,摄像机看不到任何东西,因此需要往场景中添加光源。为了跟真实世界更加接近,Threejs支持模拟不同光源,展现不同光照效果,有点光源、平行光、聚光灯、环境光等。

物体:在Threejs中,物体由形状和材质两部分组成,形状决定物品的轮廓,材质则是物体的材料和质感,不同材质构成了不同物体,比如球状,有篮球、玻璃球、水晶球等。常用的有漫反射、镜面反射两种材质,还可以引入外部图片,贴到物体表面,称为纹理贴图。

渲染

Threejs提供了重绘接口,我们有两种方式去调用接口实现重绘。

setInterval:以固定的时间间隔去调用,可以用于我们对渲染帧数要求比较高的场景,但事实上由于Javascript是单线程的,这种方式并不能100%保证相同的时间间隔调用,如果浏览器繁忙可能会导致setInterval的延迟执行。

requestAnimationFrame:让浏览器自行根据当前cpu负载等情况决定何时重绘,达到最佳帧率。

相机

透视投影:跟人眼看到的世界是一样的,近大远小。

正交投影:则远近都是一样的大小,三维空间中平行的线,投影到二维空间也一定是平行的。大部分场景都适合使用透视投影相机,因为跟真实世界的观测效果一样;在制图、建模等场景适合使用正交投影相机,方便观察模型之间的大小比例。

Threejs中的相机跟真实世界的相机不完全一样,这里相机的可见区域是一个立方体,称为相机的示景体。

外部模型

现实世界丰富多彩,不是Threejs的几种默认几何形状和材质所能表达的,实际运用中,很多时候需要用到外部模型,通过3D建模软件构建物体的三维模型并导出模型文件,Threejs导入模型文件进行使用。

光照

环境光: 所有角度看到的亮度一样,通常用来为整个场景指定一个基础亮度,没有明确光源位置。

点光源: 一个点发出的光源,照到不同物体表面的亮度线性递减。

平行光: 亮度与光源和物体之间的距离无关,只与平行光的角度和物体所在平面有关。

聚光灯:投射出的是类似圆锥形的光线。

A. 优点

  • 易于学习:非常容易上手,同样适合新手
  • 大型社区:示例多,用户多,社区丰富
  • 好的文档:强大的文档通常是一个强大的库的一个很好的指标,而 Three.js 具有出色的文档
  • 性能优势:出色的性能,能很好地执行复杂的渲染功能
  • PBR渲染:具有内置的 PBR 渲染,这使得渲染图形更加准确

B. 不足

  • 不算是游戏引擎:渲染以外的功能很少
  • 面向新手:由于 API 面向新手,因此隐藏了许多高级功能

webgl兼容性

除IE跟安卓以外,大部分浏览器支持程度较好。

开发准备

  • 产品运营需要将场景、捏脸数据给到设计和3D设计同学。
  • 建模工具Maya/3DS Max给到web研发侧的文件:

1:人物3D模型: 需要给到面部绑定有骨骼和蒙皮的 3D 人物模型源文件,可以是group也可以是拆分文件需要研发合并,文件类型一般有 .json/.fbx/.gltf 。

2:面部贴图素材: 除基本的面皮外,眉毛、胡须、腮红、纹身、眼影、唇彩,这些部位的贴图也需要单独提供,用于动态更换时做面皮的合成。
3:捏脸参数: 因为具体数据需要在web端展示,比如嘴唇大小一共有0-31档可调整,那么每一档调整的幅度时多少,调整的方式是什么。
4:动画参数:导出的模型文件包括动画名称绑定的骨骼节点的clip,追踪动画的track信息。

  • sketchfab.com: 网站上提供了很多可供免费下载的素材(例如:场景、人物、骨骼毛发),我们还可以通过它提供的在线编辑器功能,导出我们需要的文件格式。

ThreeJS实战 Demo

动画:通过AnimationMixer()获取模型的动画,clipAction获取动画的clip,通过clip的动画name找到gltf模型(图二)对应的动画进行play()操作。捏脸:通过traverse()获取全部的bone信息(图二控制台打印出了所有的骨骼信息),可以改变人物的scale、rotation等信息,bone是多层级结构,改变后所有child节点也会改变,图三我们拿到了”xiaomingNeck_05“这个关节并将其scale增大。因为动画是不间断执行的,所以需要有一个循环函数(animate)不断的更新播放器的时间(mixer.update)来更新动画,循环函数中也需要不断的渲染3D场景(renderer.render),最终就得到了动画的播放效果。场景切换:

添加环境场景的方式有很多种

添加环境:env = new RoomEnvironment()

还可以 添加网格模型到场景中:MeshPhongMaterial、planeGeometry

scene.add(env)

模型文件(以glTF为例)

1. glTF的设计思想

glTF的核心设计思路是数据和结构的分离,通过json文件存储模型的层级和索引信息,通过二进制文件存储扁平的数据体。 这样做的优点是方便数据的读写,比起传统的使用二进制数据+标记位的方式,省略了很多索引和字节判断的逻辑,使代码更加简明易懂,同时只需要一次遍历即可解析全部数据,读取效率也更高。

1.buffers数组中的某个单元,指向某个二进制文件。

2.bufferViews数组中的某个单元,指向某个buffer,并规定了读取文件的长度和偏移值,这些数据可以非常直观地转换成二进制阅读器的代码,将读出的数据写进WebGL或其他渲染api。

3.accessors指明了如何通过bufferViews来获取一组数据,并且规定了该数据的类型和范围。它是最终被几何属性引用的单位。几何结构里的坐标,索引等等,都会对应accessors里的一个下标值。

ThreeJS 源码

  1. WebGLRenderer()初始化canvas画布,loader加载模型后通过渲染器的onBeforeRender拿到model可以进行渲染、加载动画。
  2. object3D:
  • 在渲染之前 onBeforeRender: function () {},
  • 在渲染之后 onAfterRender: function () {},
  • 对变换矩阵的变换,达到缩放,旋转,移动的⽬的

applyMatrix: function ( matrix ) {
在现有的矩阵上进⾏变换,两个矩阵相乘,更新原来的变换矩阵
this.matrix.multiplyMatrices( matrix, this.matrix );
把变换矩阵分解成 位置,四元,缩放
this.matrix.decompose( this.position, this.quaternion, this.scale ); },
变换四元数
applyQuaternion: function ( q ) { this.quaternion.premultiply( q ); return this; }

  1. scene、renderers、camera这些都继承Object3D这个类,也可以自定义方法继承。

  2. 添加scene到renders中,在看到3D图形之前还需要添加camera定义用户的视角哦,controls绑定用户操作行为,动画动起来需要调用动画每一帧render模型到浏览器。

THREEJS 性能优化

  • 多使用clone方法,避免重复创建。

  • 模型不需要时在组件卸载时dispose、清除计时器(如果仅仅执行scene.remove(Mesh)只是把网格模型从场景对象的.children属性中删除,解析网格模型Mesh几何体的顶点数据通过WebGL API创建的顶点缓冲区占用的内存并不会释放。)

  • **尽量使用二进制模型:**BufferGeometry。

  • textures图片压缩、 阴影需要的才打开、threejs外部文件、项目打包体积优化。

  • 优化渲染时requestAnimationFrame中的方法,不要重复定义,注意循环,最好使用clock拿到threejs渲染完成的时间再次进行update渲染。

  • 把材质精度降低,尽量共享材质。

  • 模型拆分加载,或把模型合并,合并有消耗,尽量在编辑器下合并。

  • 异步、分片、缓存,如使用indexedDB存取模型(localStorage超出限制)。

  • 大型复杂动画使用websocket,将一些计算放到后台执行。

  • 不需要显示的,可使用WebGLRenderTarget后台渲染,需要时再加入。

  • Three****场景导入模型时,可以在保证最低清晰度的时候,降低模型的复杂度,面越多,模型越大,加载所需开销就越大。

  • 当存在多个模型动画时,根据实际情况可以将多个动画拆分,再可以对每个动画requestAnimationFrame分别设置渲染频率。

  • draco是google出的一款模型压缩工具,可将gltf格式的模型进行进一步压缩提高页面加载速的一种方法。(打包出来的压缩文件.glb需要draco_decoder.js和draco_decoder.wasm解压进行加载)

项目地址: https://github.com/xiaoaihou/Web3d-threeJs  欢迎issues