先来了解一下THREE.Points
THREE.Points
- 用来创造点的类,也用来批量管理粒子,这个类的构造函数可以接受两个参数,一个几何体和一个材质,几何体参数用来定义粒子的位置坐标,而材质参数用来格式化粒子.
(本文就结合shaderMaterial来实现自定义样式)
实现思路
需求——实现一个满屏乱飞的小点点粒子系统。

首先定义buffer几何体
假设我们要在半径为1000的球体范围空间里面创建500个小光点
let geometry = new THREE.BufferGeometry()
const num = 500
const radius = 1000
let positions = new Float32Array(num * 3)
let pulseSpeeds = new Float32Array(num)
let orbitSizes = new Float32Array(num)
let orbitSpeeds = new Float32Array(num)
数组壳子已经定义完了接下来往里面填充数据
let posIndex = 0
for (let i = 0; i < num; i++) {
positions[i * 3] = Math.random() * radius - radius / 2
positions[i * 3 + 1] = Math.random() * radius - radius / 2
positions[i * 3 + 2] = Math.random() * radius - radius / 2
pulseSpeeds[i] = pulseSpeed / 2 + (Math.random() * pulseSpeed)
orbitSizes[i] = orbitSize / 2 + (Math.random() * orbitSize)
orbitSpeeds[i] = -orbitSpeed / 2 + (Math.random() * orbitSpeed)
}
先介绍一下里面的三个参数
pulseSpeed
- 小光点缩放的速度orbitSize
- 小光点平移的范围orbitSpeed
- 小光点平移的速度
上面这段代码的大体意思就是给500个点随机的分布到半径为1000的球体范围内,然后给每个小光点设置了随机的缩放速度,平移范围,平移速度。
将属性赋值到buffer几何体的attribute里
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3))
geometry.addAttribute('pulseSpeed', new THREE.BufferAttribute(pulseSpeeds, 1))
geometry.addAttribute('orbitSize', new THREE.BufferAttribute(orbitSizes, 1))
geometry.addAttribute('orbitSpeed', new THREE.BufferAttribute(orbitSpeeds, 1))
然后是本文的重点了,定义材质中的顶点着色器。
- 第一步接受外部传进来的attribute
attribute float pulseSpeed;
attribute float orbitSpeed;
attribute float orbitSize;
有人可能会问,外部不是传进来4个attribute对象么,那是因为position这个对象THREE在框架内部默认做了接受,不需要我们自己在手动接受一次。这就是为什么我们在vertexShader里面默认可以使用position变量的原因。
- 第二步我们接受外部传进来的uniform对象
uniform float time;
uniform float size;
uniform float scale;
这里我们传递了3个变量进来,
time
- 用于动画的时间参数size
- 每个小光点的基础大小设置scale
- 每个小光点的距离屏幕距离的缩放配置(用于实现粒子的近大远小效果)
- 定义小光点们的顶点位置 先看看我们平常的定义顶点的做法
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);
其实就是对我们场景内的坐标进行一个视图模型矩阵变换,再进行一次相机矩阵的变换,转换成我们屏幕上展示的3d效果。 这里我们对他进行一个小小的变换,给position位置加上一个随时间变化的动画。
怎么实现连续的不违和的动画呢?这里我们选用三角函数,有周期性,又连贯,是个写动画的好工具
我们对position进行一个变换
vec3 transformed = animatedPosition + vec3(sin(time + orbitSpeed) * orbitSize, cos(time * orbitSpeed) * orbitSize, cos(time * orbitSpeed) * orbitSize);
这个就是我们加上动画之后的新的position位置了。
gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.);
到这里我们对于位置信息的处理已经全部完成。
- 定义小光点的渲染尺寸(给小光点附加一个周期性的缩放动画)。
float animatedSize = size * (scale / -mvPosition.z);
animatedSize *= 1.0 + sin(time * pulseSpeed);
gl_PointSize = animatedSize;
- 最后我们再把uv值存到vUv中传递到fragmentShader中用于贴图。
varying vec2 vUv;
vUv = uv;
到这里顶点着色器部分我们已经讲完了
定义片元着色器
直接贴图即可
gl_FragColor = texture2D(map, vec2(gl_PointCoord.x, 1.0 - gl_PointCoord.y));
最后我们再贪玩的给贴图加个颜色上面的处理。
gl_FragColor.rgb *= color.rgb * intensity / sqrt((vUv.x - 0.5) * (vUv.x - 0.5) + (vUv.y- 0.5) * (vUv.y - 0.5));
这句代码起啥作用呢
*color
是给贴图附加一个可配置的颜色,这样可以在外面随意配置小光点的着色了。
后面那一长串又是干嘛的呢,就是贴图越接近中心越接近白色,用来模仿一个光点的发光的假象,当然传进来的贴图就是个漂亮的光点可以不进行下面那部分操作。
定义材质
有了uniforms,vertexShader,fragmentShader定义ShaderMaterial就很简单了,具体的一些参数看看api就可以了
最后我们定义粒子系统对象加到scene里面就好了
let floater = new THREE.Points(geometry, material)
scene.add(floater)
当然里面具体的参数还需要大家自己去微调,都是些细节操作。越细腻动画效果就越好。
到这里我们就实现了一个满屏乱飞的小光点粒子系统,大家还可以自行实验,修改顶点坐标的动画方式,也可以实现不断上浮的小光点动画,以及各种能想到的一些粒子动画效果,全看脑洞了
是不是很简单!

感谢大家的观看!有错的地方欢迎指正,单纯是个人的理解不一定说的都对。如需转载,请注明个出处哈