携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情 >
在三维场景中,我们可以通过粒子实现烟花,星空,雨滴等炫酷效果,每颗粒子都是由两个三角面片构成的;本案例实现如封面动图效果;
step 1 创建粒子
材质上sizeAttenuation属性表示粒子是否近大远小效果,默认是打开的
/**
* Objects
*/
// 创建geometry
const geometry = new THREE.BufferGeometry();
// Material
const pointMaterial = new THREE.PointsMaterial({
// 设置大小
size: 0.02,
// 设置颜色
color: new THREE.Color('#fff'),
})
// 设置粒子数量
const count = 5000;
// 存放粒子位置信息
const posAttribute = new Float32Array(count * 3)
// 每一个位置信息中包含 xyz三个轴的坐标,所以存放位置时数组的长度为原有的粒子长度*3
for (let i = 0; i <= count * 3; i++) {
// Math.random()的随机数为0-1,则Math.random() - 0.5的随机数为-0.5-0.5之间,根据自己需要展示的范围*对应的数字
posAttribute[i] = (Math.random() - 0.5) * 10
}
// 为geometry设置计算所得的坐标信息
geometry.setAttribute('position', new THREE.BufferAttribute(posAttribute, 3))
// 创建粒子
const point = new THREE.Points(geometry, pointMaterial)
// 添加至场景中
scene.add(point)
完成步骤一,我们可以得到图下效果
step 2 为粒子添加合适的贴图
贴图网址:
在这里放了下图贴图下载的网址,按需使用;
材质中几个需要特别说明的属性
depthTest: 深度测试,WGL绘制时,会测试那个粒子更靠前,在其后的粒子不会被绘制,前面的会被绘制,也应用于其他Mesh中;
depthWrite: 有一个叫做深度缓冲区的地方,如果将该属性修改为false,则模型不会进入深度缓冲区中,也不会被深度监听了;
blending: 混合模式,THREE.AdditiveBlending为相加混合,默认为NormalBlending,也就是法线混合;
// 为材质中增加了一些属性
const pointMaterial = new THREE.PointsMaterial({
// 怕贴图看不清改变了大小
size: 0.2,
color: new THREE.Color('#fff'),
// alphaMap贴图 因为我们的图片是黑白图,用alpha可以实现透明
alphaMap: texture1,
// alphaMap贴图捆绑实现
transparent: true,
// 修复边界遮挡问题
depthWrite: false,
blending: THREE.AdditiveBlending,
})
此时效果如下图,我设置了一个❤的贴图
step 3 为粒子添加随机色
// 颜色也是三位
for (let i = 0; i <= count * 3; i++) {
posAttribute[i] = (Math.random() - 0.5) * 10
colorAttribute[i] = Math.random()
}
// 属性设置
geometry.setAttribute('color', new THREE.BufferAttribute(colorAttribute, 3))
// 需要去除调原有的颜色设置
const pointMaterial = new THREE.PointsMaterial({
// color: new THREE.Color('#fff'),
// 打开顶点着色器
vertexColors: true
})
step 4 将粒子展开平铺
// 获取clock 只是为了拿到一个稳定的累加数字
const clock = new THREE.Clock();
let num = clock.getElapsedTime()
// 重置Y轴坐标
for (let i = 0; i <= count; i++) {
let numI = i * 3;
const x = geometry.attributes.position.array[numI + 0]
geometry.attributes.position.array[numI + 1] = Math.sin(num+x)
}
// 打开位置更新
geometry.attributes.position.needsUpdate = true;
基本效果已经实现
step 4 为粒子添加自动旋转动画
通过render中方法实现
function renderScene() {
let num = clock.getElapsedTime()
// 不断更新y轴旋转值
point.rotation.y = num * 0.02
// 更新轨道控制 实现鼠标可操控
controls.update();
requestAnimationFrame(renderScene)
renderer.render(scene, camera)
}
如果有哪里描述不准确或者有问题,欢迎大佬指正!
(≖ᴗ≖)✧