前言

WebGL是一种3D绘图协议,这种绘图协议允许javascript和openGL结合起来,WebGL可以为canvas提供3d硬件加速(使用GPU在浏览器渲染3d模型和场景)。WebGL是openGL的一种实现,是可以在支持WebGL的浏览器中进行3d渲染。由于浏览器的跨平台性,可以使用WebGL在多种设备多个平台展示3d效果。那么我们可以用WebGL做什么呢?

  1. 数据可视化

  2. AR VR

  3. 3d游戏动画等

在现代前端技术发展中,使用WebGL创建3d效果是一种不可或缺的能力。由于webGL本身API只能绘制最普通的点、线、三角形,所以为了绘制大型场景和模型,我们需要选用一些框架帮助开发,比如three.js。

Three.js是对WebGL的API抽象化和封装化的js库,不必关心WebGL怎么渲染3d图形,并且在渲染中加入了各种优化,提高了性能。Three.js包含数学库,支持交互,扩展性强,还可以进行SVG,CSS3D等渲染,在WebGL不兼容的版本可以有回退解决方案。下图是three.js对WebGL做的封装:

勋章系统设计

勋章系统开发选型

勋章系统是对用户粘性提升的一种积极刺激行为,分为样式展示(简单图片)--行为触发(达到某种条件)--颁发勋章(提供3d勋章)--勋章升级(不同等级勋章提供不同展示)--趣味性(移动端控制旋转特效)。在开发勋章系统中有几个重点:

  • 技术选型 因为是在app中展示,要考虑安卓和ios的差异性,同时还需要在web端展示,所以最好的选择就是用前端的WebGL方式来做3d勋章系统,在app中通过webview引入该勋章网页。在框架选择中,three.js的兼容性功能性都是接受过业界业务考验的,我们选择了three.js当我们的勋章系统框架。

  • 模型选择 既然是3d勋章,3d模型的选择会影响开发周期和渲染效果,调研了three.js支持的3d模型格式:

Three.JS支持格式  特点 优缺点
JSON(*.js/ *.json) 专门为Three.js自己设计的JSON格式,你可以使用它以声明的方式定义模型,及模型材质和动画。 在app中的webview解析json比较慢
OBJ和MTL(*.obj/ *.mtl) OBJ是一种简单的三维文件格式,只用来定义对象的几何体。 对象不支持动画,还需要搭配mtl来载入材质
Collada (*.dae) 用来定义XML类文件中数字内容的格式。差不多所有的三维软件和渲染引擎都支持这个格式。 使用最广,兼容性好
STL (*.stl) 立体成型术 。 广泛用于快速成型。 生成速度快,但是一般是3d打印机使用
FBX (*.fbx) 在max、maya、softimage等软件间进行模型、材质、动作和摄影机信息的互导,复用性比较好。 多平台支持,但是生成在部分平台会被转为mesh
CTM (*.ctm) 由openCTM创建的格式。可以用来压缩存储表示三维网格的三角形面片。 文件压缩效果好,压缩算法理解难度较高
VTK(*.vtk) Visualization Tookit定义的文件格式,用来指定顶点和面。 可支持节点比较多,但threejs仅支持旧格式
PLY (*.ply) 多边形文件格式。 3d打印机使用

加上ui组使用c4d软件能轻松导出dae文件,最终选择了以xml为基础的dae的3d模型。

运行Three.js

Three.js是3d渲染,在3d渲染编程中包括:场景,相机,渲染器(物体,光源,纹理)。下图是three.js的运行过程:

要想使用three.js很简单,通过简单的js引用或者npm包下载即可。然后通过加载相应的解析模块,来载入个人模型或者控制器。

import 'three';import 'three/examples/js/loaders/ColladaLoader2';import 'three/examples/js/loaders/MTLLoader';

在threejs中初始化场景,相机,灯光:

function init() {   initRender()   initScene()   initCamera()   initLight()}

导入模型,并加载到场景:

function loadModel() {   mesh = new THREE.Mesh()let mtl = new THREE.MTLLoader()let loader = new THREE.ColladaLoader()   mtl.load("../model/yemaozi.mtl", function (result) {       result.preload()let { materials } = result       loader.load("../model/yemaozi.dae", function (dae) {for (let key in dae.library.materials) {let name = dae.library.materials[key].nameif (materials[name]) {Object.assign(dae.library.materials[key].build, materials[name])               }if (name === 'font') {                   dae.library.materials[key].build.blending = THREE.NoBlending                   dae.library.materials[key].build.needsUpdate = true               }           }           mesh = dae.scene.children[0].clone()           scene.add(mesh)       })   })

}

最后是最关键的渲染(包含动效):

// 动效function animate() {   requestAnimationFrame(animate)   mesh.rotation.y +=0.01   render()}

展示效果如下:

勋章系统优化

支持换肤功能

  • 多加入一个包含材质和纹理信息的mtl文件,通过不同的mtl材质提供换肤功能 ,接口传递的数据为:

{   // 材质文件   image3DMaterial: "http://xxx.mtl",   // gzip压缩过的模型文件   image3DMoudle: "http://xxx.gz",   // 已获得图片   imageGot: "http://xxx.png",   // 未获得图片   imageNotget: "http://xxx.png"}

生成模型文件较大,下载时间过长

  • 出于体验优化,一般大文件传输需要gzip处理,目前使用的cdn服务暂不支持文件的gzip(后期会迁移到新cdn)。所以前端用nodejs写一个预压缩脚本,将dae和mtl文件压缩并上传到cdn,同时同步更新到服务端的数据库中;拿到数据后,解压则是通过webWorker多线程,使用pako.js库进行gzip解压。

下载解析开销很大,并且3d模型占用内存过多

  • 目前产品需求是只有等级5的勋章,分两步优化:

    // 尝试从临时缓存中读取模型if (models[currentMedalInfo.id]) {    scene.add(models[currentMedalInfo.id]);    modelsList.splice(modelsList.findIndex((id) => currentMedalInfo.id === id), 1);    modelsList.push(currentMedalInfo.id);    resolve();return;  }
    
    let medalDataMap, medalDataCache, modelData, materialData;if (indexedDBSupport) {
    
        medalDataMap = await localforage.getItem('medalDataMap');    medalDataMap = medalDataMap || {};    medalDataCache = medalDataMap[currentMedalInfo.id];if (medalDataCache) {// 如果材质地址没变则直接从indexedDB读取,否则清理掉if (medalDataCache.image3DMaterial === materialUrl) {        materialData = await localforage.getItem(materialUrl);      } else {        localforage.removeItem(medalDataCache.image3DMaterial);      }// 模型同上if (medalDataCache.image3DMoudle === modelUrl) {        modelData = await localforage.getItem(modelUrl);      } else {        localforage.removeItem(medalDataCache.image3DMoudle);      }    }// 储存最新的信息    medalDataMap[currentMedalInfo.id] = currentMedalInfo;    localforage.setItem('medalDataMap', medalDataMap);  }
    
    // 如果不支持或indexedDB中没有则加载if (!materialData) {    materialData = loadResource({ url: materialUrl });  }if (!modelData) {    modelData = loadResource({url: modelUrl, type: isDae ? 'text' : 'arraybuffer', onProgress: (progress) =>{        progressCB(progress.loaded / progress.total * (isDae ? 100 : 80));      }    });  }
    • 内存中只会存储最近5次的解析过的模型,当有新的模型要加入,通过LRU策略,对于最长时间没有使用的模型内存会释放。

    • 数据会存在IndexedDB,每次请求先从内存读取,内存中不存在从IndexedDB读取,都不存在走网络请求,并且会把新数据通过key-value存入IndexedDB。

有些过老机型不支持WebGL,模型加载失败

  • 在获取数据时候,会返回两张图片(没获得等级勋章图片和获得等级勋章图片),通过图片展示当做优雅降级处理方法。

用户体验

  • 增加重力感应和粒子特效。

最终效果

总结

5G时代的来临,让网络传输和资源加载速度更胜一筹,在3D效果的制作可以采用WebGL这种跨平台兼容性高的方法。选用Three.js来快速开发WebGL项目,能为用户体验和业务需求提供多样化的选择。

作者简介

孙森 2018年加入网易传媒,高级前端开发工程师,目前主要做pc,移动端,小程序等版本迭代和升级优化工作,热爱足球和coding。

3d环形图片展示 js_网易公开课Three.js实践 勋章系统相关推荐

  1. 如何做到像百度云或者网易公开课一样动态更换APP启动图

    http://www.code4app.com/forum.php?mod=viewthread&tid=7632&extra=page%3D2%26filter%3Dsortid%2 ...

  2. 《网易公开课》也能被拿来练习python爬虫?离谱~

    本篇博客是第四遍学习协程相关知识,我们在之前内容积累的基础上,新增加一个异步请求库,该库名称为 aiohttp. 为了给大家演示 aiohttp 如何与 asyncio 进行搭配,本文采用代码对比形式 ...

  3. ionic2入门教程(三)高仿网易公开课(1)

    Ionic2系列之高仿网易公开课(1) 0.登录界面实现截图和官方图片对比 我的 官方 1.新建一个blank项目 打开cmd,输入ionic start Ionic-NetEaseOpenCours ...

  4. Python语言程序设计之urllib.request抓取页面,网易公开课之《麻省理工学院公开课:算法导论》

    Python语言用urllib.request模块抓取页面非常简单,再将抓取的页面内容用re模块解析,找出自己想要的东西.下面就就此方法来抓取网易公开课之<麻省理工学院公开课:算法导论>, ...

  5. 麻省理工计算机导论公开课,网易公开课给大一新生“量身订做”精品课程

    速途网讯 耶鲁大学的<金融理论>.哈佛大学的<计算机导论>.可汗学院的<线性代数>,除了专业课,你还可以感受哈佛的<幸福课>,还可以透过麻省理工学院&l ...

  6. Auto.js Pro安卓免ROOT引流脚本开发系列教程27网易公开课(5)-UI界面构建

    课程内容 脚本前端UI界面的构建 创建话术输入框(随机话术) 创建勾选框(性别选择.话术前加入昵称.话术后添加随机符号表情) 开发文档 在线文档 APP名称 网易公开课 APP版本 安卓客户端:v6. ...

  7. Auto.js Pro安卓免ROOT引流脚本开发系列教程23网易公开课(1)-前言

    APP名称 网易公开课 APP版本 安卓客户端:v6.8.1 APP简介 网易公开课提供来自世界一流名校和著名机构的上万集精品视频课程,涵盖各类热门领域,与Web版保持同步更新.速度流畅,画面高清.支 ...

  8. Auto.js Pro安卓免ROOT引流脚本开发系列教程26网易公开课(4)-关注用户

    APP_关注用户() 返回值类型 说明 布尔型 true,关注成功 false,关注失败 等待个人资料页出现(判断是否在个人资料页) 判断关注按钮节点是否存在 判断是否已关注 关注成功后随机延时 开发 ...

  9. Auto.js Pro安卓免ROOT引流脚本开发系列教程25网易公开课(3)-取用户性别

    APP_取用户性别() 返回值类型 说明 整数型 返回值 性别 0 女 1 男 2 无 等待个人资料页出现(判断是否在个人资料页) 判断性别节点是否存在 在性别节点范围内取色 根据色值判断性别 开发文 ...

最新文章

  1. 线上分享 | 增长思维:如何选择最优增长模式?
  2. 基与datatable的分页
  3. 解决 Invalid character found in method name. HTTP method names must be tokens 异常信息
  4. Matlab求方差,均值
  5. android,解决手动创建的桌面快捷方式无法跳转到制定的activity的问题,提示未安装应用程序
  6. 智云健康上市在即:长期亏损,美年健康俞熔已退出,未来难言乐观
  7. BIOS知识枝桠——简称释义(按名称排序)
  8. Linux_2022/7/19_Day1
  9. 每日一结(11.1)
  10. 震坤行:以成就客户为导向,用专业打造数字化供应链采购流程
  11. 2017年18岁的北京文科状元父母都是外交官,关于高考,他说出了我们竭力回避的戳心真相
  12. MS2109高清采集卡 HDMI转USB2.0免驱,支持MJPEG YUV数据格式 demo原理图介绍
  13. 清除pycharm残留文件
  14. mysql不包含模糊查询
  15. 5-33 有理数加法
  16. centos7安装yearning
  17. python 打包发布(含静态文件)
  18. 轻码云大沙拉出位:用验证码直戳创客痛点
  19. Hibernate出现java.lang.IllegalArgumentException: org.hibernate.QueryException
  20. Java excel导入导出

热门文章

  1. 计划会议要开始了,产品负责人却没来…
  2. OBS鉴权实现的宝典秘籍,速拿!
  3. 在家办公怎么弄?华为云DevCloud宝典一看就懂——项目管理篇
  4. 跟繁琐的命令行说拜拜!Gerapy分布式爬虫管理框架来袭!
  5. 【DigSci 科学数据挖掘大赛】冠军方案关键技术解析
  6. 来吧,1分钟带你玩转Kafka
  7. hpunix查看oracle监听,hp-ux 网络查看
  8. MySQL指定存储引擎命令_MySQL常用指令(2)——存储引擎
  9. ajax是宏任务还是微任务,(滴滴面试)事件循环Event Loop及微任务和宏任务的执行过程详解...
  10. java语言复制数组的四种方法