在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
本文主要讲述通过 Three.js + Blender 技术栈,实现 Meta 公司炫酷的 3D 动态 Logo,内容包括基础模型圆环、环面扭结、管道及模型生成、模型加载、添加动画、添加点击事件、更换材质等。 背景
什么是元宇宙元宇宙 Metaverse 一词源于 元宇宙的内涵是吸纳了信息革命 实现效果进入正题,先来看看本文示例的实现效果。
开发实现
开发之前我们先观察一下 试炼一:THREE.TorusGeometry
语法示例:
试炼二:THREE.TorusKnotGeometry
语法示例:
试炼三:THREE.TubeGeometry
代码示例 // ... 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虽然使用
建模教程 逛
用Blender建模使用 加载依赖 <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); }
加载Logo模型使用 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); }); 添加材质本文 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 }); } } }); }) 添加动画
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(); } }) }) }); 添加动画之后,不要忘了要在 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); } 展示加载进度
<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) }); 实现效果 点击更换材质监听页面的点击事件,通过 //声明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);
加载人物模型人物模型的加载流程和 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,该网站有有上百种人物和上千种动作可自由组合, 总结本文中涉及到的主要知识点包括:
参考资料[2]. ThreeJs认识材质 [3]. Three之Animation初印象 [4]. 什么是元宇宙? 作者:dragonir 本文地址:https://www.cnblogs.com/dragonir/p/15574412.html 到此这篇关于Three.js实现脸书元宇宙3D动态Logo的文章就介绍到这了,更多相关Three.js3D动态Logo 内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论