查看原文
其他

【第1879期】《Web 3D来了!体验不一样的春节活动》技术篇

腾讯P&Pdesign 前端早读课 2020-08-24

前言

Web 3D有点打开另一扇门了。今日早读文章由@腾讯PPdesign授权分享。

@P&PDesign全称Platform & Publish Design,隶属于腾讯互动娱乐发行线下的设计中心,团队由视觉、交互、多媒体、UI开发人才构成,负责为腾讯游戏平台体系和发行业务提供体验和服务设计,包括WeGame平台、QQ游戏平台、游戏本地化设计、端游手游助手、移动社区等业务;致力于游戏平台、UI、社区设计领域的研究和沉淀

正文从这开始~~

在2020年春节来临之际,我们WeGame推出春节促销活动。随着 CINEMA 4D 越来越被设计师普及,3D逐渐成为了当前的一种设计趋势,相比于平面页面,3D能给用户带来更真实的交互场景从而获得更好的体验。最近我们也有了一定的3D技术储备,因此将Web 3D技术应用到了在此次春节活动中。

项目展示

Web 3D相比传统2D展示氛围及带入感更强。我们使用的是threeJS库,完成threejs 3D微场景搭建的同时丰富页面中的交互,增强场景春节氛围感。

体验活动,请到文末通过“阅读原文”查看

关键技术

在进入场景时我们需要控制摄像机的轨迹来达到环视3D模型的效果,这一效果需要通过弧度算取欧拉角旋转来实现的。(如下图摄像机运动轨迹的曲线绘制方法,坐标v0为3D模型)

1、如何通过两点计算出运动轨迹

计算三维向量中心点坐标:

代码

  1. /**

  2. * @desc 获取两个向量之间的中心点坐标

  3. * @param {Object} v1 向量1

  4. * @param {Object} v2 向量2

  5. */

  6. getVectorCenter: function(v1, v2){

  7. /**

  8. * [add] [divideScalar] 均为Vector3自带方法函数

  9. *

  10. * 公式:

  11. * x = (x1 + x2) / 2

  12. * y = (y1 + y2) / 2

  13. */

  14. var v1c = v1.clone();

  15. var v2c = v2.clone();

  16. return v1c.add(v2c).divideScalar(2);

  17. }

其中Vector3中提供基础方法

根据圆心和中心点坐标,得到一个点的射线:

代码

  1. // 通过Ray,得到射线段


  2. var rayLine = new THREE.Ray(new THREE.Vector3(0,0,0), vectorCenter)


  3. // 在射线段先随意定义一个射线顶点

  4. // at可以设置射线的距离,并且返回Vector3坐标

  5. // rayLine.at(1) == vectorCenter, 将向量中心点距离圆心长度 * n


  6. var vtop = rayLine.at(10)

根据射线顶点 + 起始点与终点坐标,可以随意选取线段内的控制点坐标:

根据起始点+终点+控制点坐标,可以绘制贝塞尔曲线,仍然存在几个优化点:

当起点与终点象限重合时,仍然会被绘制曲线

即使通过两点获得出一条曲线后,会发现相机在位置1时,移动的路径可能不会出现问题,但是相机在位置2的时候,此时相机2距离视点位置较近,不需要作曲线。

解决方法

获得相机位置点到视点的直线距离,再减去半径获得差值,当差值小于0时,代表相机位置距离视点小于半径则直接做直线运动,再用差值除以直径就获得出比例。

  1. var len = starPos.distanceTo(endPos) - three3D.radius <= 0 ? 0 : starPos.distanceTo(endPos) - three3D.radius;

  2. var ratio = len / ( three3D.radius * 2 ) <= 1 ? len / ( three3D.radius * 2 ) : 1;

2、雪花效果

春节活动在冬天,因此我们需要雪花来衬托冬天的氛围。制作3D下雪场景需要如下几个步骤:

2.1 首先需要制作一个雪花的3D模型

使用的图片材质:

制作一个雪花3D模型代码:

  1. var texture = new THREE.TextureLoader().load("./images/snow0.png");

  2. //样式化粒子的THREE.PointCloudMaterial材质

  3. var material = new THREE.PointsMaterial({

  4. size: size,

  5. transparent: transparent,

  6. opacity: opacity,

  7. vertexColors: vertexColors,

  8. sizeAttenuation: sizeAttenuation,

  9. color: color,

  10. map: texture,

  11. depthTest: false //设置解决透明度有问题的情况

  12. });

2.2 随机生成多个雪花的坐标
  1. var cloud;

  2. var geom = new THREE.Geometry(); //存放粒子数据的网格

  3. var range = 2400; //生成雪花的坐标范围

  4. for (var i = 0; i < 700; i++) { //生成700个雪花

  5. //添加顶点的坐标

  6. var particle = new THREE.Vector3(Math.random() * range - range / 2, Math.random() * range - range / 2, Math.random() * range - range / 2);

  7. particle.velocityY = (0.1 + Math.random() / 5) * 20;

  8. particle.velocityX = ((Math.random() - 0.5) / 3) * 10;

  9. particle.velocityZ = ((Math.random() - 0.5) * 3) * 10;

  10. geom.vertices.push(particle);

  11. var color = new THREE.Color(0xffffff);

  12. geom.colors.push(color);

  13. }

  14. //生成模型,添加到场景当中

  15. cloud = new THREE.Points(geom, material);

  16. cloud.verticesNeedUpdate = true;

  17. scene.add(cloud);

2.3 雪花飘落动画
  1. Snow.prototype.snowRender = function () {

  2. //产生雪动画效果

  3. var vertices = cloud.geometry.vertices;

  4. vertices.forEach(function (v, index) {

  5. v.y = v.y - (v.velocityY) * .3;

  6. v.x = v.x - (v.velocityX) * .05;

  7. v.z = v.z - (v.velocityZ) * .05;

  8. if (v.y <= -1200) v.y = 1200;

  9. if (v.x <= -400 || v.x >= 400) v.velocityX = v.velocityX * -0.1;

  10. if (v.z <= -400 || v.z >= 400) v.velocityZ = v.velocityZ * -0.1;

  11. });

  12. //设置实时更新网格的顶点信息

  13. cloud.geometry.verticesNeedUpdate = true;

  14. }

3、音乐动画

通过用欢快的音乐衬托出春节喜气洋洋的节日氛围。

代码

  1. // 音符从小变大

  2. var scaleSize,

  3. endScale = 0.015;

  4. if (obj.position.y > endCoord.y) {

  5. scaleSize = 0;

  6. } else if (obj.position.y < endCoord.y) {

  7. scaleSize = ((obj.position.y - startCoord.y) / (endCoord.y - startCoord.y)) * endScale;

  8. }



  9. // 控制透明度

  10. var opacityVal;

  11. if (obj.position.y < startCoord.y + 3 || obj.position.y > endCoord.y) {

  12. opacityVal = 0;

  13. } else if (((startCoord.y + 3) <= obj.position.y) && (obj.position.y < (startCoord.y + 25))) {

  14. opacityVal = 1;

  15. }else {

  16. opacityVal = 1 - ((obj.position.y - (startCoord.y + 25)) / (endCoord.y - startCoord.y - 25));

  17. }

  18. opacityVal = opacityVal > 0 ? opacityVal : 0


  19. // 如果音符飘到顶,则重置回到初始值

  20. if (obj.position.y > endCoord.y) {

  21. obj.position.y = startCoord.y;

  22. obj.position.z = startCoord.z;

  23. obj.material.opacity = 0

  24. }

4、全屏幕适配

设计稿为标准的1920px*1080px尺寸,但在小屏幕或笔记本下会出现内容显示不全的问题,给用户带来了不友好的体验。

以宽度1920px,高度1080px为参考基准,我们进行了全设备的兼容,步骤如下:

4.1、分析屏幕尺寸,以参考基础为标准,一共有五种情况:
  • A情况:“宽等于1920px,高等于1080px”,

  • B情况:“宽大于1920px,高大于1080px”

  • C情况:“宽小于1920px,高小于1080px”

  • D情况:“宽大于1920px,高小于1080px”

  • E情况:“宽小于1920px,高大于1080px”

核心点:获取当前浏览器尺寸。

4.2、根据不同屏幕尺寸情况进行整体缩放。

我们获取到此时浏览器实际宽高后,需要分辨出此时浏览器尺寸是“扁的”还是“高的”。

扁的情况

高的情况

设置一个基准比例:

BaseRate = 1920/1080 = 1.777;

设置一个实时比例:

Rate = PageWidth/PageHeight;

所以,“扁的”情况为Rate > BaseRate,“高的”情况为 Rate < BaseRate。

核心点:获取出当前浏览器是“扁的”还是“高的”情况,并计算缩放比例系数。

4.3、通过比例系数进行缩放和内容重新定位。

当宽高都大于1920px*1080px时:不进行放大缩放,但要对内容进行垂直和水平居中定位。

当宽高都小于1920px*1080px时:

若为“扁的”情况:则需要对高度进行缩放,并且垂直居中。

若为“高的”情况:则需要对宽度进行缩放,并且水平居中。

同理,在各种情况下,我们需要对其缩放系数进行计算后进行缩放,同时还需要对内容进行垂直或水平方向的重新定位。

同时我们还需要使用window.onresize函数进行实时监听,保证用户在手动缩放浏览器大小的时候也能进行适配。

核心点:缩放后内容的重新定位,window.onresize函数的实时监听。

这样我们在各种屏幕大小下,都能满足非常好的用户阅读体验,保证用户在浏览页面的过程中全部信息的获取。

后期性能测试

测试目的:了解实际项目在Web3D中的性能影响,将数据提供给开发进行兼容处理。

测试机型:

根据测试提供的wegame用户机型配置数据来对比,此次测试主要分为三种机型配置 4G显存(中高配)、2G显存(中配)、1G显存(中低配)。

测试点:FPS、CPU、GPU与体验感受。

测试方向:1、页面首次载入数据。2、页面3D交互数据。

测试结果:

1、低配机器测试过程中GPU使用率在95%+,但是页面浏览流畅,并无明显卡顿感,FPS总体维持在35以上。在中配机器测试过程中GPU使用率平均在32%左右,页面流畅无卡顿,FPS维持在55左右。

2、通过3D场景的体验中发现,低配机器CPU平均占用在25%左右,在页面首次载入的时候,CPU占用会有2-3秒左右飙升到40%。中低配机器CPU平均占用在16%左右,首次载入时会有2-3秒飙升到37%。

测试总结:项目在测试过程中发现,在非客户端内嵌的情况下,基本都可以流畅的体验web3d,因此我们也放弃2d兼容方案。

项目优化

项目后期因增加了设计师提供的模型,使得项目资源大小增加了10M,因此采用Draco 压缩方案进行优化3d模型的资源大小。Draco是一个用于压缩、解压缩 3D 几何网格和点云的开源库,为改善 3D 图形存储和传输而设计。

使用draco可以对 glTF 格式进一步的压缩,原本10M的GLTF文件被压缩到1M。

以往使用视频方式做kv,通常会准备一个入场资格,循环视频,大小往往超过5m,此次春节活动仅仅只需要加载1M的模型即可。同时视频仅仅只能展示,而web3D方案交互性更加丰富。

项目总结

Web 3D主要攻克技能点
  • 根据rgb主站场景进行点击曲线路径控制

  • 3D雪花氛围效果

  • 模型效果测试反馈

  • 根据实际3D场景效果进行交互优化

  • 物体的圆形运动轨迹

  • 模型贴图与模型动画控制

  • 将雪花效果改成漂浮动画z

  • 云动画 + 简单漂浮动画

后期功能完善
  • 模型还原、交互还原

  • 入场动画

  • 场景增加文字信息

  • 模型加载与引导动画

  • 性能测试准备

参考文档

1、模型外发光及模型禁止反光材质(外发光效果):https://segmentfault.com/a/1190000019449497 2、实现出光源效果:https://threejs.org/examples/?q=light#webgllightsspotlights 3、模型优化方案(使用draco方案进行压缩):https://threejs.org/docs/index.html#examples/zh/loaders/DRACOLoader 4、模型材质发光设置:https://segmentfault.com/a/1190000019449497 http://www.yanhuangxueyuan.com/doc/Three.js/MeshStandardMaterial.html https://threejs.org/docs/index.html#api/zh/materials/MeshStandardMaterial 5、雪花参考demo:https://threejs.org/examples/#webglpointssprites

关于本文 作者:@腾讯P&Pdesign 原文:https://mp.weixin.qq.com/s/AuM5naNxxYk1ko-SxJafSw

@腾讯PPdesign曾分享过


【第1800期】利用过渡动效打造沉浸式的体验 —【Web篇】


为你推荐


【第1875期】如何在页面极速渲染3D模型


【第1876期】从构建进程间缓存设计 谈 Webpack5 优化和工作原理


体验活动,通过“阅读原文”查看

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存