百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Three.js 的 3D 粒子动画群星送福(3dmax粒子动画案例)

toyiye 2024-04-04 11:27 19 浏览 0 评论

粒子动画“ 这个词大家可能经常听到,那什么是粒子动画呢?


粒子是指原子、分子等组成物体的最小单位。在 2D 中,这种最小单位是像素,在 3D 中,最小单位是顶点。


粒子动画不是指物体本身的动画,而是指这些基本单位的动画。因为是组成物体的单位的动画,所以会有打碎重组的效果。


本文我们就来学习下 3D 的粒子动画,做一个群星送福的效果:


思路分析

3D 世界中,物体是由顶点构成,3 个顶点构成一个三角形,然后给三角形贴上不同的纹理,这样就是一个三维模型。


图片

也就是说,3D 模型是由顶点确定的几何体(Geometry),贴上不同的纹理(Material)所构成的物体(Mesh 等)。


之后,把 3D 物体添加到场景(Scene)中,设置一个相机(Camera)角度去观察,然后用渲染器(Renderer)一帧帧渲染出来,这就是 3D 渲染流程。


3D 物体是由顶点构成,那让这些顶点动起来就是粒子动画了,因为基本粒子动了,自然就会有打碎重组的效果。


在“群星送福”效果中,我们由群星打碎重组成了福字,实际上就是群星的顶点运动到了福字的顶点,由一个 3D 物体变成了另一个 3D 物体。


那么群星的顶点从哪里来的?福字的顶点又怎么来呢?


群星的顶点其实是随机生成的不同位置的点,在这些点上贴上星星的贴图,就是群星效果。


福字的顶点是加载的一个 3D 模型,解析出它的顶点数据拿到的。


有了两个 3D 物体的顶点数据,也就是有了动画的开始结束坐标,那么不断的修改每个顶点的 x、y、z 属性就可以实现粒子动画。


这里的 x、y、z 属性值的变化不要自己算,用一些动画库来算,它们支持加速、减速等时间函数。Three.js 的动画库是 Tween.js。


总之,3D 粒子动画就是顶点的 x、y、z 属性的变化,会用动画库来计算中间的属性值。由一个物体的顶点位置、运动到另一个物体的顶点位置,会有种打碎重组的效果,这也是粒子动画的魅力。


思路理清了,那我们来具体写下代码吧。


代码实现

如前面所说,3D 的渲染需要一个场景(Scene)来管理所有的 3D 物体,需要一个相机(Camera)在不同角度观察,还需要渲染器(Renderer)一帧帧渲染出来。


这部分是基础代码,先把这部分写好:


创建场景:


const scene = new THREE.Scene();

创建相机:


const width = window.innerWidth;

const height = window.innerHeight;

const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

相机分为透视相机和平行相机,我们这里用的透视相机,也就是近大远小的透视效果。要指定可以看到的视野角度(45)、宽高比(width/height)、远近范围(0.1 到 1000)这 3 种参数。


调整下相机的位置和观察方向:


camera.position.set(100, 0, 400);

camera.lookAt(scene.position);

然后是渲染器:


const renderer = new THREE.WebGLRenderer();

renderer.setSize(width, height);

document.body.appendChild(renderer.domElement);

渲染器要通过 requestAnimationFrame 来一帧帧的渲染:


function render() {

renderer.render(scene, camera);

requestAnimationFrame(render);

}

render();

准备工作完成,接下来就是绘制星空、福字这两种 3D 物体,还有实现粒子动画了。


绘制星空

星空不是正方体、圆柱体这种规则的几何体,而是由一些随机的顶点构成的,这种任意的几何体使用缓冲几何体 BufferGeometry 创建。


为啥这种由任意顶点构成的几何体叫缓冲几何体呢?


因为顶点在被 GPU 渲染之前是放在缓冲区 buffer 中的,所以这种指定一堆顶点的几何体就被叫做 BufferGeometry。


我们创建 30000 个随机顶点:


const vertices = [];

for ( let i = 0; i < 30000; i ++ ) {

const x = THREE.MathUtils.randFloatSpread( 2000 );

const y = THREE.MathUtils.randFloatSpread( 2000 );

const z = THREE.MathUtils.randFloatSpread( 2000 );

vertices.push( x, y, z );

}

这里用了 Three.js 提供的工具 MathUtils 来生成 0 到 2000 的随机值。


然后用这些顶点创建 BufferGeometry:


const geometry = new THREE.BufferGeometry();

geometry.setAttribute( 'position', new THREE.Float32BufferAttribute(vertices, 3));

给 BufferGeometry 对象设置顶点位置,指定 3 个数值(x、y、z)为一个坐标。


然后创建这些顶点上的材质(Material),也就是星星的贴图:


图片

const star = new THREE.TextureLoader().load('img/star.png');

const material = new THREE.PointsMaterial( { size: 10, map: star });

顶点有了,材质有了,就可以创建 3D 物体了(这里的 3D 物体是 Points)。


const points = new THREE.Points( geometry, material );

scene.add(points);

看下渲染的效果:


图片

静态的没 3D 的感觉,我们让每一帧转一下,改下 render 逻辑:


function render() {

renderer.render(scene, camera);

scene.rotation.y += 0.001;


requestAnimationFrame(render);

}

再来看一下:


3D 星空的感觉有了!


接下来我们来做粒子动画:


3D 粒子动画

3D 粒子动画就是顶点的动画,也就是 x、y、z 的变化。


我们先来实现个最简单的效果,让群星都运动到 0,0,0 的位置:


起始点坐标就是群星的的本来的位置,通过 getAttribute('position') 来取。动画过程使用 tween.js 来计算:


const startPositions = geometry.getAttribute('position');


for(let i = 0; i< startPositions.count; i++) {

const tween = new TWEEN.Tween(positions);


tween.to({

[i * 3]: 0,

[i * 3 + 1]: 0,

[i * 3 + 2]: 0

}, 3000 * Math.random());


tween.easing(TWEEN.Easing.Exponential.In);

tween.delay(3000);

tween.onUpdate(() => {

startPositions.needsUpdate = true;

});

tween.start();

}

每个点都有 x、y、z 坐标,也就是下标为 i3、i3+1、i*3+2 的值,我们指定从群星的起始位置运动到 0,0,0 的位置。


然后指定了时间函数为加速(Easing.Exponential.In),3000 ms 后开始执行动画。


每一帧渲染的时候要调用下 Tween.update 来计算最新的值:


function render() {

TWEEN.update();

renderer.render(scene, camera);

scene.rotation.y += 0.001;


requestAnimationFrame(render);

}

每一帧在绘制的时候都会调用 onUpdate 的回调函数,我们在回调函数里把 positions 的 needsUpdate 设置为 true,就是告诉 tween.js 在这一帧要更新为新的数值再渲染了。


第一个粒子动画完成!


来看下效果(我把这个效果叫做万象天引):


所有的星星粒子都集中到了一个点,这就是粒子动画典型的打碎重组感。


接下来,只要把粒子运动到福字的顶点就是我们要做的“群星送福”效果了。


福字模型的顶点肯定不能随机,自己画也不现实,这种一般都是在建模软件里画好,然后导入到 Three.js 来渲染,


我找了这样一个福字的 3D 模型:


图片

模型是 fbx 格式的,使用 FBXLoader 加载:


const loader = new THREE.FBXLoader();

loader.load('./obj/fu.fbx', function (object) {

const destPosition = object.children[0].geometry.getAttribute('position');


});

回调参数就是从 fbx 模型加载的 3D 物体,它是一个 Group(多个 3D 物体的集合),取出第 0 个元素的 geometry 属性,就是对应的几何体。


这样,我们就拿到了目标的顶点位置。


把粒子动画的结束位置改为福字的顶点就可以了:


const cur = i % destPosition.count;

tween.to({

[i * 3]: destPosition.array[cur * 3],

[i * 3 + 1]: destPosition.array[(cur * 3 + 1)],

[i * 3 + 2]: destPosition.array[(cur * 3 + 2)]

}, 3000 * Math.random());

如果开始顶点位置比较多,超过的部分从 0 的位置再来,所以要取余。


大功告成!


这就是我们想要的粒子效果:


完整代码上传到了 github:https://github.com/QuarkGluonPlasma/threejs-exercize


也在这里贴一份:


<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title></title>

<style>

body {

margin: 0;

}

</style>

<script src="./js/three.js"></script>

<script src="./js/tween.js"></script>

<script src="./js/FontLoader.js"></script>

<script src="./js/TextGeometry.js"></script>

<script src="./js/FBXLoader.js"></script>

<script src="./js/fflate.js"></script>

</head>

<body>

<script>

const width = window.innerWidth;

const height = window.innerHeight;

const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);


const scene = new THREE.Scene();

const renderer = new THREE.WebGLRenderer();


camera.position.set(100, 0, 400);

camera.lookAt(scene.position);


renderer.setSize(width, height);

document.body.appendChild(renderer.domElement)


function create() {

const vertices = [];

for ( let i = 0; i < 30000; i ++ ) {

const x = THREE.MathUtils.randFloatSpread( 2000 );

const y = THREE.MathUtils.randFloatSpread( 2000 );

const z = THREE.MathUtils.randFloatSpread( 2000 );

vertices.push( x, y, z );

}

const geometry = new THREE.BufferGeometry();

geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );


const star = new THREE.TextureLoader().load('img/star.png');

const material = new THREE.PointsMaterial( { size: 10, map: star });


const points = new THREE.Points( geometry, material );

points.translateY(-100);

scene.add(points);


const loader = new THREE.FBXLoader();

loader.load('./obj/fu.fbx', function (object) {

const startPositions = geometry.getAttribute('position');

const destPosition = object.children[0].geometry.getAttribute('position')

for(let i = 0; i< startPositions.count; i++) {

const tween = new TWEEN.Tween(startPositions.array);

const cur = i % destPosition.count;

tween.to({

[i * 3]: destPosition.array[cur * 3],

[i * 3 + 1]: destPosition.array[cur * 3 + 1],

[i * 3 + 2]: destPosition.array[cur * 3 + 2]

}, 3000 * Math.random());

tween.easing(TWEEN.Easing.Exponential.In);

tween.delay(3000);


tween.start();


tween.onUpdate(() => {

startPositions.needsUpdate = true;

});

}

} );

}


function render() {

TWEEN.update();

renderer.render(scene, camera);

scene.rotation.y += 0.001;


requestAnimationFrame(render);

}

create();

render();

</script>

</body>

</html>

总结

粒子动画是组成物体的基本单位的运动,对 3D 来说就是顶点的运动。


我们要实现“群星送福”的粒子动画,也就是从群星的顶点运动到福字的顶点。


群星的顶点可以随机生成,使用 BufferGeometry 创建对应的几何体。福字则是加载创建好的 3D 模型,拿到其中的顶点位置。


有了开始、结束位置,就可以实现粒子动画了,过程中的 x、y、z 值使用动画库 Tween.js 来计算,可以指定加速、减速等时间函数。


粒子动画有种打碎重组的感觉,可以用来做一些很炫的效果。理解了什么是粒子动画、动的是什么,就算是初步掌握了。


我摘下漫天繁星,想给大家送一份福气,新的一年一起加油!

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码