Three.js 之 12 Particles 粒子效果

您所在的位置:网站首页 粒子背景特效怎么做 Three.js 之 12 Particles 粒子效果

Three.js 之 12 Particles 粒子效果

2024-03-13 09:38| 来源: 网络整理| 查看: 265

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

本系列为 Three.js journey 教程学习笔记。包含以下内容

Three.js 之 1 Animation 动画 Three.js 之 2 Camera 相机 Three.js 之 3 画布与全屏 Three.js 之 4 Geometry 几何体 Three.js 之 5 debug UI Three.js 之 6 Texture 纹理 Three.js 之 7 Materials 材质 Three.js 之 8 炫酷的 3D Text Three.js 之 9 Light 光 Three.js 之 10 Shadow 投影 Three.js 之 11 Haunted House 恐怖鬼屋 Three.js 之 12 Particles 粒子效果 Three.js 之 13 Galaxy 银河效果生成器 Three.js 之 14 Raycaster 光线投射 Three.js 之 15 Scroll based animation 基于页面滚动的动画 Three.js 之 16 Physics 物理引擎 Three.js 之 17 Import Model 导入模型

未完待续

粒子特效

听到粒子特效,是不是无比期待?可以用它实现非常多的效果如星空、烟雾、雨、灰尘、火等。

粒子特效的优势是即使使用了成百上千的例子,也能保证比较高的帧率。缺点是每个粒子都由一个始终面向相机的平面(两个三角形)组成。

创建粒子像创建几何体一样简单,我们使用 PointsMaterial 材质,它不会创建几何体而是创建非常多的点 Points。下面我们开始吧。

第一个粒子效果 Geometry 几何体

创建一个球体

/** * Particles */ // geometry const sphereGeometry = new THREE.SphereGeometry(1, 32, 32) PointsMaterial 点材质

创建点材质

// material const pointMaterial = new THREE.PointsMaterial({ size: 0.02, sizeAttenuation: true, })

有2个属性

.size : Number 设置点的大小。默认值为1.0。 .sizeAttenuation : Boolean 指定点的大小是否因相机深度而衰减。(仅限透视摄像头。)默认为true。 使用 Points

之前我们都是使用 Mesh 网格几何体,现在使用点 Points。

const particles = new THREE.Points(sphereGeometry, pointMaterial) scene.add(particles)

效果如下

改变 sizeAttenuation 和 size 效果如下

在线 demo 链接

可扫码访问

demo 源码

完整代码如下

import * as THREE from 'three' import './style.css' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import * as dat from 'lil-gui' import stats from '../common/stats' import { listenResize, dbClkfullScreen } from '../common/utils' // Canvas const canvas = document.querySelector('#mainCanvas') as HTMLCanvasElement // Scene const scene = new THREE.Scene() /** * Particles */ // geometry const sphereGeometry = new THREE.SphereGeometry(1, 32, 32) // material const pointMaterial = new THREE.PointsMaterial({ size: 0.02, sizeAttenuation: true, }) const particles = new THREE.Points(sphereGeometry, pointMaterial) scene.add(particles) /** * Lights */ const ambientLight = new THREE.AmbientLight('#ffffff', 0.4) scene.add(ambientLight) // Size const sizes = { width: window.innerWidth, height: window.innerHeight, } // Camera const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100) camera.position.set(2, 1.8, 2) const controls = new OrbitControls(camera, canvas) controls.enableDamping = true // controls.autoRotateSpeed = 0.2 controls.zoomSpeed = 0.3 // Renderer const renderer = new THREE.WebGLRenderer({ canvas, }) renderer.setSize(sizes.width, sizes.height) renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) listenResize(sizes, camera, renderer) dbClkfullScreen(document.body) // Animations const tick = () => { stats.begin() controls.update() pointMaterial.needsUpdate = true // Render renderer.render(scene, camera) stats.end() requestAnimationFrame(tick) } tick() /** * Debug */ const gui = new dat.GUI() gui.add(controls, 'autoRotate') gui.add(controls, 'autoRotateSpeed', 0.1, 10, 0.01) gui.add(pointMaterial, 'size', 0.01, 0.1, 0.001) gui.add(pointMaterial, 'sizeAttenuation') 自定义几何体

为了创建一个自定义几何体,我们需要使用 BufferGeometry 类,并且添加 postion 属性,可以参考我们之前学习的 Three.js 之 4 Geometry 几何体 章节。

// geometry const particlesGeometry = new THREE.BufferGeometry() const count = 5000 const positions = new Float32Array(count * 3) // 每个点由三个坐标值组成(x, y, z) for (let i = 0; i < count * 3; i += 1) { positions[i] = (Math.random() - 0.5) * 5 } particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))

我们尝试将粒子个数设置的更高,仍然能得到非常好的性能和帧率,如下图,将

const count = 500000

仍然满帧运行

接下来我们设置个数为 5000,并设置 pointMaterial.size 为 0.1

Color, map and alpha map

首先为材质设置颜色

pointMaterial.color = new THREE.Color('#ff88cc')

再增加纹理贴图,我们使用如下的贴图

/** * Textures */ const textureLoader = new THREE.TextureLoader() const particleTexture = textureLoader.load('https://gw.alicdn.com/imgextra/i3/O1CN01DO6Ed61QtcMKsVnK2_!!6000000002034-2-tps-56-56.png')

赋给材质

pointMaterial.map = particleTexture

效果如下

这些好看的纹理贴图来自 Kenney 的站点,在这里可以找到更多。这个圆环就是来自这个包中 www.kenney.nl/assets/part…

但仔细看,可以发现这个例子没有透明,挡住了后面的粒子,如下图

我们设置 alphaMap 和 transparent

pointMaterial.alphaMap = particleTexture pointMaterial.transparent = true

透明的效果有了,但还是能看到偶尔出现的边缘,如下图

这是因为粒子在创建的时候,WebGL 认为它们在同一层,没法区分哪一个在哪一个之前。这个问题稍为复杂一点,有多种方式可以解决。

alphaTest

.alphaTest : Float

设置运行 alphaTest 时要使用的alpha值。如果不透明度低于此值,则不会渲染材质。默认值为0。

就是让 WebGL 明白什么时候根据像素的透明度不进行渲染。默认为 0 即总是会渲染,如果我们设置一个很小的值,则如何 alpha 为 0 时不会被渲染

pointMaterial.alphaTest = 0.001

仔细看这个方案不是很完美,还是能看到一些毛边

depthTest

是否在渲染此材质时启用深度测试。默认为 true。

这个遮挡的背景问题正是因为开启了 depthTest,WebGL 不知道哪个在前哪个在后,导致的,所以我们可以关掉这个深度测试。

// pointMaterial.alphaTest = 0.001 pointMaterial.depthTest = false

看起来效果不错。但是因为我们关闭了深度测试,会导致另一个bug,如果我们创建一个几何体,那么这个几何体就会总是在这些粒子之后了。

// cube const cube = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshStandardMaterial()) scene.add(cube)

如下图,这是一个很奇怪的透视效果。如果没有其他几何体的话,使用 depthTest 关闭的方案就足够了,接下来我们再看看其他方案

depthWrite

渲染此材质是否对深度缓冲区有任何影响。默认为 true。

WebGL 在渲染是会检测当前渲染的深度和之前已经渲染的物体深度的对比,已渲染的深度会被缓存在 depth buffer 中。这是我们设置 depthWrite 为 false 相当于告知发现更近的粒子时 WebGL 不要在将其写入 depth buffer 中。

pointMaterial.depthWrite = false

Blending

混合,设置将叠加的部分的效果

pointMaterial.depthWrite = false pointMaterial.blending = THREE.AdditiveBlending

我们增加一些粒子,可以看到叠加的部分变得更加高亮,我们可以用这个效果制作烟火、火焰等

要注意的时,这个效果可能会带来性能问题

替换颜色

我们一起看看怎么设置每个粒子的颜色

我们需要给 particlesGeometry 设置另一组属性

const particlesGeometry = new THREE.BufferGeometry() const count = 20000 const positions = new Float32Array(count * 3) // 每个点由三个坐标值组成(x, y, z) const colors = new Float32Array(count * 3) // 每个颜色由三个rgb组成 for (let i = 0; i < count * 3; i += 1) { positions[i] = (Math.random() - 0.5) * 10 colors[i] = Math.random() } particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) particlesGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)) ... pointMaterial.vertexColors = true

vertexColors 是否使用顶点着色。默认值为 false。

需要注意的是,原有的 color 仍然是生效的,两种颜色做了混合,现在我们注释掉原有颜色

// pointMaterial.color = new THREE.Color('#ff88cc')

在线 demo 链接

可扫码访问

demo 源码

Animate 动画

有很多种方式制作动画

直接控制 Points 对象

因为 Points 类是集成字 Object3D,所以和 Mesh 类一样,可以控制其位置、旋转、放大等属性

// Animations const clock = new THREE.Clock() const tick = () => { stats.begin() const elapsedTime = clock.getElapsedTime() particles.position.x = 0.1 * Math.sin(elapsedTime) controls.update() pointMaterial.needsUpdate = true // Render renderer.render(scene, camera) stats.end() requestAnimationFrame(tick) }

并增加坐标轴助手 axesHelper,便于观察

const axesHelper = new THREE.AxesHelper(1) scene.add(axesHelper)

接下来我们试着控制每一个粒子

通过修改 attributes 制作动画

每个粒子独立动画,就需要控制粒子的属性了。接下来我们做一个波浪形的动画效果

先将上面的动画代码注释掉。添加如下代码,设置每个点的 position,让其上下浮动

// Animations const clock = new THREE.Clock() const tick = () => { stats.begin() const elapsedTime = clock.getElapsedTime() // particles.position.x = 0.1 * Math.sin(elapsedTime) for (let i = 0; i < count; i += 1) { particlesGeometry.attributes.position.setY(i, Math.sin(elapsedTime)) } particlesGeometry.attributes.position.needsUpdate = true controls.update() // pointMaterial.needsUpdate = true // Render renderer.render(scene, camera) stats.end() requestAnimationFrame(tick) } tick()

注意要设置 position update particlesGeometry.attributes.position.needsUpdate = true

效果如下

接下来,需要将 Y 轴的运动与 X 位置关联起来,以便让其产生波动的效果。

// Animations const clock = new THREE.Clock() const tick = () => { stats.begin() const elapsedTime = clock.getElapsedTime() // particles.position.x = 0.1 * Math.sin(elapsedTime) for (let i = 0; i < count; i += 1) { const x = particlesGeometry.attributes.position.getX(i) particlesGeometry.attributes.position.setY(i, Math.sin(elapsedTime + x)) } particlesGeometry.attributes.position.needsUpdate = true controls.update() // pointMaterial.needsUpdate = true // Render renderer.render(scene, camera) stats.end() requestAnimationFrame(tick) } tick()

效果如下

在线 demo 链接

可扫码访问

demo 源码

看似很完美,但是应该尽量避免使用这个技术,因为非常占用性能。我们在每帧设置计算了所有粒子的位置,数量少时性能还可以,但是粒子数量变大后,会非常占用计算机的性能。更好的解法是使用自定义 shader 的方案,我们后续会进行详细学习。

小结

本节我们学习了粒子效果,学习了 PointsMaterial/Points 类生成粒子,如何自定义粒子位置颜色等,研究了粒子的遮挡关系,最后学习了一点控制粒子的动画。Keep going!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3