Three.js實現臉書元宇宙3D動態Logo效果
目錄
- 背景
- 什麼是元宇宙
- 實現效果
- 試煉一:THREE.TorusGeometry
- 試煉二:THREE.TorusKnotGeometry
- 試煉三:THREE.TubeGeometry
- 試煉四:Blender + Three.
- 用Blender建模
- 載入Logo模型
- 新增材質
- 展示載入進度
- 點選更換材質
- 載入人物模型
- 總結
- 參考資料
本文主要講述通過 Three.js + Blender 技術棧,實現 Meta 公司炫酷的 3D 動態 Logo,內容包括基礎模型圓環、環面扭結、管道及模型生成、模型載入、新增動畫、新增點選事件、更換材質等。
背景
Facebook
近期將其母公司改名為 Meta
,宣佈正式開始進軍 元宇宙 🪐
Three.js
+ Blender
技術棧,實現 Meta
公司炫酷的 3D
動態 Logo
,內容包括基礎模型圓環、環面扭結、管道及模型生成、模型載入、新增動畫、新增點選事件、更換材質等。
什麼是元宇宙
元宇宙 Metaverse 一詞源於 1992
年尼爾斯蒂芬森的 《雪崩》
,該書描述了一個平行於現實世界的虛擬世界 Metaverse
,所有現實生活中的人都有一個網路分身 Avatar
。維基百科
對元宇宙的描述是:通過虛擬增強的物理現實,呈現收斂性和物理永續性特徵的,基於未來網際網路,具有連結感知和共享特徵的 3D
虛擬空間。
元宇宙的內涵是吸納了資訊革命 5G/6G
web3.0
、人工智慧革命,以及 VR
、AR
、MR
,特別是遊戲引擎在內的虛擬現實技術革命的成果,向人類展現出構建與傳統物理世界平行的全息數字世界的可能性;引發了資訊科學、量子科學,數學和生命科學的互動,改變科學正規化;推動了傳統的哲學、社會學甚至人文科學體系的突破;囊括了所有的數字技術。正如電影 《頭號玩家》
的場景,在未來某一天,人們可以隨時隨地切換身份,自由穿梭於物理世界和數字世界,在虛擬空間和時間節點所構成的元宇宙中生活學習。
實現效果
進入正題,先來看看本文示例的實現效果。
🔗
線上預覽:https://dragonir..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,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.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),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,compose,camera,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));
// 半球光源:建立室外效果更加自然的光源
light = new THREE.HemisphereLight(0xefefef);
light.position.set(0,20,0);
scene.add(light);
// 平行光
light = new THREE.DirectionalLight(0x2d2d2d);
light.position.set(0,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;
grhttp://www.cppcns.comid.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 = trwww.cppcns.comue;
container.appendChild(renderer.domElement);
// 新增鏡頭控制器
controls = new THREE.OrbitControls(camera,renderer.domElement);
controls.target.set(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.Rahttp://www.cppcns.comycaster(); 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[0ltZqmO]; 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/dragonir/p/15574412.html
到此這篇關於Three.js實現臉書元宇宙3D動態Logo的文章就介紹到這了,更多相關Three.js3D動態Logo 內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!