背景
Facebook
近期将其母公司改名为 Meta
,发表正式开始进军 元宇宙
畛域。本文次要讲述通过 Three.js
+ Blender
技术栈,实现 Meta
公司炫酷的 3D
动静 Logo
,内容包含根底模型圆环、环面扭结、管道及模型生成、模型加载、增加动画、增加点击事件、更换材质等。
什么是元宇宙
元宇宙 Metaverse 一词源于 1992
年尼尔·斯蒂芬森的 《雪崩》
,该书形容了一个平行于事实世界的虚拟世界 Metaverse
,所有现实生活中的人都有一个网络分身 Avatar
。维基百科
对元宇宙的形容是:通过虚构加强的物理事实,出现收敛性和物理持久性特色的,基于将来互联网,具备链接感知和共享特色的 3D
虚拟空间。
元宇宙的外延是吸纳了信息反动5G/6G
、互联网反动web3.0
、人工智能反动,以及VR
、AR
、MR
,特地是游戏引擎在内的虚拟现实技术反动的成绩,向人类展现出构建与传统物理世界平行的全息数字世界的可能性;引发了信息科学、量子迷信,数学和生命科学的互动,扭转迷信范式;推动了传统的哲学、社会学甚至人文科学体系的冲破;囊括了所有的数字技术。正如电影《头等玩家》
的场景,在将来某一天,人们能够随时随地切换身份,自在穿梭于物理世界和数字世界,在虚拟空间和工夫节点所形成的元宇宙中生存学习。
实现成果
进入正题,先来看看本文示例的实现成果。
在线预览:https://dragonir.github.io/3d-meta-logo (因为模型较大,加载进度可能比拟迟缓,须要急躁期待)
开发实现
留神:上述示例动图展现的是试炼四,不想看试错过程(试炼一、试炼二、试炼三)的,可间接跳转到试炼四段落查看具体实现流程。失败流程中都列出了难点,晓得解决方案的大佬请在评论区不吝赐教。
开发之前咱们先察看一下 Meta Logo
,能够发现它是一个圆环通过对折扭曲造成的,因而实现它的时候能够从实现圆环开始。
试炼一:THREE.TorusGeometry
Three.js
提供的根底几何体 THREE.TorusGeometry
(圆环),它是一种看起来像甜甜圈 的简略图形。主要参数:
radius
:可选。定义圆环的半径尺寸。默认值是1
。tube
:可选。定义圆环的管子半径。默认值是0.4
。radialSegments
:可选。定义圆环长度方向上的分段数。默认值是8
。tubularSegments
:可选。定义圆环宽度方向上的分段数。默认值是6
。arc
:可选。定义圆环绘制的长度。取值范畴是0
到2 *
。默认值是2 *
(一个残缺的圆)。
语法示例:
THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments, arc);
失败
:没有找到扭曲圆环的办法。
试炼二:THREE.TorusKnotGeometry
THREE.TorusKnotGeometry
能够用来创立三维环面扭结,环面扭结是一种比拟特地的结,看上去像一根管子绕着它本人旋转了几圈。主要参数:
radius
:可选。设置残缺圆环的半径,默认值是1
。tube
:可选。设置管道的半径,默认值是0.4
。radialSegments
:可选。指定管道截面的分段数,段数越多,管道截面圆越润滑,默认值是8
。tubularSegments
:可选。指定管道的分段数,段数越多,管道越润滑,默认值是64
。p
:可选。决定几何体将绕着其旋转对称轴旋转多少次,默认值是2
。q
:可选。决定几何体将绕着其外部圆环旋转多少次,默认值是3
。
语法示例:
THREE.TorusKnotGeometry(radius, tube, radialSegments, tubularSegments , p, q);
失败
:没找到可能管制手动扭曲水平的办法。
试炼三:THREE.TubeGeometry
THREE.TubeGeometry
沿着一条三维的样条曲线拉伸出一根管。你能够指定一些定点来定义门路,而后应用 THREE.TubeGeometry
创立这根管。主要参数:
path
:该属性用一个THREE.SplineCurve3
对象来指定管道该当遵循的门路。segments
:该属性指定构建这个管所用的分段数。默认值为64
.门路越长,指定的分段数应该越多。radius
:该属性指定管的半径。默认值为1
.radiusSegments
:该属性指定管道圆周的分段数。默认值为8
,分段数越多,管道看上去越圆。closed
:如果该属性设置为true
,管道的头和尾会连起来,默认值为false
。
代码示例
// ...var controls = new function () { // 点的地位坐标 this.deafultpoints = [ [0, 0.4, -0.4], [0.4, 0, 0], [0.4, 0.8, 0.4], [0, 0.4, 0.4], [-0.4, 0, 0], [-0.4, 0.8, -0.4], [0, 0.4, -0.4] ] this.segments = 64; this.radius = 1; this.radiusSegments = 8; this.closed = true; this.points = []; this.newPoints = function () { var points = []; for (var i = 0; i < controls.deafultpoints.length; i++) { var _x = controls.deafultpoints[i][0] * 22; var _y = controls.deafultpoints[i][1] * 22; var _z = controls.deafultpoints[i][2] * 22; points.push(new THREE.Vector3(_x, _y, _z)); } controls.points = points; controls.redraw(); }; this.redraw = function () { redrawGeometryAndUpdateUI(gui, scene, controls, function() { return generatePoints(controls.points, controls.segments, controls.radius, controls.radiusSegments, controls.closed); }); };};controls.newPoints();function generatePoints(points, segments, radius, radiusSegments, closed) { if (spGroup) scene.remove(spGroup); spGroup = new THREE.Object3D(); var material = new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: false }); points.forEach(function (point) { var spGeom = new THREE.SphereGeometry(0.1); var spMesh = new THREE.Mesh(spGeom, material); spMesh.position.copy(point); spGroup.add(spMesh); }); scene.add(spGroup); return new THREE.TubeGeometry(new THREE.CatmullRomCurve3(points), segments, radius, radiusSegments, closed);}// ...
勉强胜利
:然而管道连成的圆环不够圆,实现完满的圆弧须要准确的坐标,临时没找到坐标计算方法。
试炼四:Blender + Three.js
尽管应用 THREE.TubeGeometry
能够勉强实现,然而成果并不好,要实现圆滑的环,须要为管道增加准确的扭曲圆环曲线门路函数。因为数学能力无限 ️
,临时没找到扭曲圆弧门路计算的办法。因而决定从建模层面解决。
胜利
:然而手残的我应用 Blender
建模破费了大量的工夫 。
建模教程
逛 B站
的时候发现了这位大佬发的宝藏视频,刚好解决了本人的难题。
传送门:【动静设计教程】AE+blender能怎么玩?脸书元宇宙Meta动静logo已齐全解析,100%学会
用Blender建模
应用 Blender
进行建模,并导出可携带动画的 fbx
格局,导出的时候不要遗记勾选 烘焙动画
选项。
加载依赖
<script src="./assets/libs/three.js"></script><script src="./assets/libs/loaders/FBXLoader.js"></script><script src="./assets/libs/inflate.min.js"></script><script src="./assets/libs/OrbitControls.js"></script><script src="./assets/libs/stats.js"></script>
场景初始化
var container, stats, controls, compose, camera, scene, renderer, light, clickableObjects = [], mixer, mixerArr = [], manMixer;var clock = new THREE.Clock();init();animate();function init() { container = document.createElement('div'); document.body.appendChild(container); // 场景 scene = new THREE.Scene(); scene.transparent = true; scene.fog = new THREE.Fog(0xa0a0a0, 200, 1000); // 透视相机:视场、长宽比、近面、远面 camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 4, 16); camera.lookAt(new THREE.Vector3(0, 0, 0)); // 半球光源:创立室外成果更加天然的光源 light = new THREE.HemisphereLight(0xefefef); light.position.set(0, 20, 0); scene.add(light); // 平行光 light = new THREE.DirectionalLight(0x2d2d2d); light.position.set(0, 20, 10); light.castShadow = true; scene.add(light); // 环境光 var ambientLight = new THREE.AmbientLight(0xffffff, .5); scene.add(ambientLight); // 网格 var grid = new THREE.GridHelper(100, 100, 0xffffff, 0xffffff); grid.position.set(0, -10, 0); grid.material.opacity = 0.3; grid.material.transparent = true; scene.add(grid); renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.outputEncoding = THREE.sRGBEncoding; renderer.setSize(window.innerWidth, window.innerHeight); // 背景色设置为通明 renderer.setClearAlpha(0); // 开启暗影 renderer.shadowMap.enabled = true; container.appendChild(renderer.domElement); // 增加镜头控制器 controls = new THREE.OrbitControls(camera, renderer.domElement); controls.target.set(0, 0, 0); controls.update(); window.addEventListener('resize', onWindowResize, false); // 初始化性能插件 stats = new Stats(); container.appendChild(stats.dom);}// 屏幕缩放function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight);}
想理解场景初始化的具体流程,可浏览我的另一篇文章《应用three.js实现炫酷的酸性格调3D页面》。
加载Logo模型
应用 FBXLoader
加载模型,并设置模型的地位和大小。
var loader = new THREE.FBXLoader();loader.load('assets/models/meta.fbx', function (mesh) { mesh.traverse(function (child) { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); mesh.rotation.y = Math.PI / 2; mesh.position.set(0, 1, 0); mesh.scale.set(0.05, 0.05, 0.05); scene.add(mesh);});
增加材质
本文 Logo
应用的是 MeshPhysicalMaterial
材质,它是一种 PBR
物理材质,能够更好的模仿光照计算,相比拟高光网格材质 MeshPhongMaterial
渲染成果更真切。应用 THREE.TextureLoader
为材质增加 map
属性来加载模型贴图。下图是金属质感的纹理贴图。
var texLoader = new THREE.TextureLoader();loader.load('assets/models/meta.fbx', function (mesh) { mesh.traverse(function (child) { if (child.isMesh) { if (child.name === '贝塞尔圆') { child.material = new THREE.MeshPhysicalMaterial({ map: texLoader.load("./assets/images/metal.png"), metalness: .2, roughness: 0.1, exposure: 0.4 }); } } });})
增加动画
AnimationMixer
对象是场景中特定对象的动画播放器。当场景中的多个对象独立动画时,能够为每个对象应用一个AnimationMixer
。AnimationMixer
对象的clipAction
办法生成能够管制执行动画的实例。
loader.load('assets/models/meta.fbx', function (mesh) { mesh.animations.map(item => { mesh.traverse(child => { // 因为模型中有多个物体,并且各自有不同动画,示例中只为贝塞尔圆这个网格增加动画 if (child.name === '贝塞尔圆') { let mixer = new THREE.AnimationMixer(child); mixerArr.push(mixer); let animationClip = item; animationClip.duration = 8; let clipAction = mixer.clipAction(animationClip).play(); animationClip = clipAction.getClip(); } }) })});
增加动画之后,不要忘了要在 requestAnimationFrame
中更新动画。
function animate() { renderer.render(scene, camera); // 取得前后两次执行该办法的工夫距离 let time = clock.getDelta(); // 更新logo动画 mixerArr.map(mixer => { mixer && mixer.update(time); }); // 更新人物动画 manMixer && manMixer.update(time); stats.update(); requestAnimationFrame(animate);}
展现加载进度
FBXLoader
同时返回两个回调函数,能够像上面这样应用,用来展现模型加载过程展现以及加载失败的逻辑实现。
<div class="loading" id="loading"> <p class="text">加载进度<span id="progress">0%</span></p><div>
var loader = new THREE.FBXLoader();loader.load('assets/models/meta.fbx', mesh => {}, res => { // 加载过程 let progress = (res.loaded / res.total * 100).toFixed(0); document.getElementById('progress').innerText = progress; if (progress === 100) { document.getElementById('loading').style.display = 'none'; }}, err => { // 加载失败 console.log(err)});
实现成果
点击更换材质
监听页面的点击事件,通过 HREE.Raycaster
拿到以后点击对象,为了展现例子,我为点击对象更换了一种材质 THREE.MeshStandardMaterial
,并赋予它随机的 color
色彩、metalness
金属质感以及 roughness
毛糙水平。
//申明raycaster和mouse变量var raycaster = new THREE.Raycaster();var mouse = new THREE.Vector2();function onMouseClick(event) { // 通过鼠标点击的地位计算出raycaster所须要的点的地位,以屏幕核心为原点,值的范畴为-1到1. mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; // 通过鼠标点的地位和以后相机的矩阵计算出raycaster raycaster.setFromCamera(mouse, camera); // 获取raycaster直线和所有模型相交的数组汇合 let intersects = raycaster.intersectObjects(clickableObjects); if (intersects.length > 0) { console.log(intersects[0].object) let selectedObj = intersects[0].object; selectedObj.material = new THREE.MeshStandardMaterial({ color: `#${Math.random().toString(16).slice(-6)}`, metalness: Math.random(), roughness: Math.random() }) }}window.addEventListener('click', onMouseClick, false);
更多对于网格材质的常识,可参考文章开端的链接。
加载人物模型
人物模型的加载流程和 Logo
模型加载流程是一样的。我增加了一个正在施展龟派气功的人物,没想到与 Logo
模型的旋转动画十分符合 。
loader.load('assets/models/man.fbx', function (mesh) { mesh.traverse(function (child) { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); mesh.rotation.y = Math.PI / 2; mesh.position.set(-14, -8.4, -3); mesh.scale.set(0.085, 0.085, 0.085); scene.add(mesh); manMixer = new THREE.AnimationMixer(mesh); let animationClip = mesh.animations[0]; let clipAction = manMixer.clipAction(animationClip).play(); animationClip = clipAction.getClip();}, res => { let progress = (res.loaded / res.total * 100).toFixed(0); document.getElementById('progress').innerText = progress + '%'; if (Number(progress) === 100) { document.getElementById('loading').style.display = 'none'; }}, err => { console.log(err)});
本文示例人物模型来源于mixamo.com,该网站有有上百种人物和上千种动作可自由组合,收费
下载。大家能够筛选本人喜爱的人物和动画动作来练习 Three.js
。
总结
本文中波及到的次要知识点包含:
THREE.TorusGeometry
:圆环。THREE.TorusKnotGeometry
:环面扭结。THREE.TubeGeometry
:管道。Blender
: 建模。FBXLoader
: 加载模型,显示加载进度。TextureLoader
:加载材质。THREE.AnimationMixer
:加载动画。THREE.Raycaster
:捕捉点击模型。
残缺代码:https://github.com/dragonir/3d-meta-logo
参考资料
- [1]. 应用three.js实现炫酷的酸性格调3D页面
- [2]. ThreeJs意识材质
- [3]. Three之Animation初印象
- [4]. 什么是元宇宙?
作者:dragonir 本文地址:https://www.cnblogs.com/drago...