呀哈喽!这里是 alphardex。
MeshSurfaceSampler,是 three.js 的表面采样器,通过它,我们可以在一个Mesh
的表面拾取一定数量的随机点位,从而实现一些炫酷的粒子特效,请看以下的效果(全屏最佳):
https://code.juejin.cn/pen/7164008029766025223
本文将简要地介绍一下实现这个效果的思路
PS:本文标题 neta 了《孤独摇滚》的 live 曲《吉他与孤独与蓝色星球》
表面采样
首先用 kokomi.js 的Text3D类来创建 3D 文字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const font = await kokomi.loadFont();
const text = "J U E J I N";
const t3d = new kokomi.Text3D( this, text, font, { size: 0.6, height: 0.2, curveSegments: 120, bevelEnabled: true, bevelThickness: 0.03, bevelSize: 0.02, bevelOffset: 0, bevelSegments: 5, }, { baseMaterial: new THREE.ShaderMaterial(), vertexShader, fragmentShader, materialParams: { side: THREE.DoubleSide, }, } ); t3d.mesh.geometry.center(); t3d.addExisting();
|
利用 kokomi.js 封装好的函数sampleParticlesPositionFromMesh来采样文字表面的点位数据
1 2 3 4
| const sampledPos = kokomi.sampleParticlesPositionFromMesh( t3d.mesh.geometry, 8000 );
|
同时也获取下每个点位的下标
1
| const pIndexs = kokomi.makeBuffer(sampledPos.length / 3, (v, k) => v);
|
将点位位置数据和下标数据存储至BufferGeometry
里
1 2 3
| const geometry = new THREE.BufferGeometry(); geometry.setAttribute("position", new THREE.BufferAttribute(sampledPos, 3)); geometry.setAttribute("pIndex", new THREE.BufferAttribute(pIndexs, 1));
|
新建一个微粒对象THREE.Points
,并定义好ShaderMaterial
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const uj = new kokomi.UniformInjector(this);
const material = new THREE.ShaderMaterial({ vertexShader, fragmentShader, side: THREE.DoubleSide, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false, uniforms: { ...uj.shadertoyUniforms, uColor: { value: new THREE.Color("#d1a657"), }, uPointSize: { value: 4, }, }, });
this.update(() => { uj.injectShadertoyUniforms(material.uniforms); });
const im = new THREE.Points(geometry, material); this.scene.add(im);
|
顶点着色器里传入微粒大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| uniform float iTime; uniform vec2 iResolution; uniform vec2 iMouse;
varying vec2 vUv;
uniform float uPointSize;
attribute float pIndex;
varying float vOpacity; varying vec3 vPosition;
void main(){ vec3 p=position; gl_Position=projectionMatrix*modelViewMatrix*vec4(p,1.);
vUv=uv; gl_PointSize=uPointSize; }
|
片元着色器里传入微粒颜色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| uniform float iTime; uniform vec2 iResolution; uniform vec2 iMouse;
varying vec2 vUv;
uniform vec3 uColor;
varying float vOpacity; varying vec3 vPosition;
void main(){ vec2 p=gl_PointCoord;
vec3 col=uColor;
gl_FragColor=vec4(col,1.); }
|
可以看到被采样的点均匀地分布在了文字之上,cool!让它动起来吧!
气泡效果
首先编写片元着色器,利用spot
函数来把微粒变成气泡状
1 2 3
| float shape=spot(p,.1,2.5);
gl_FragColor=vec4(col,shape);
|
将 uPointSize 微粒大小略微调大点,我们会看到下面的效果
接下来我们要在顶点着色器中变换顶点的 y 轴坐标,用到了随机函数和噪声函数,这里贴一下随机函数的实现吧,噪声函数请自行查看Github 链接
1 2 3
| float random(float n){ return fract(sin(n)*43758.5453123); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| vec3 distort(vec3 p){ float t=iTime*.25; float rndz=(random(pIndex)+cnoise(vec2(pIndex*.1,t*.5))); p.y+=fract((t+rndz)*.5); return p; }
void main(){ vec3 p=position; p=distort(p); gl_Position=projectionMatrix*modelViewMatrix*vec4(p,1.);
vOpacity=random(pIndex); vPosition=p; }
|
在片元着色器中能够根据顶点着色器传递的vPosition
和vOpacity
来实现气泡的随机大小和透明度效果
1 2 3 4 5 6 7 8 9 10 11 12
| float saturate(float a){ return clamp(a,0.,1.); }
void main(){ ... float alpha=1.-saturate(abs(vPosition.y*1.2)); shape*=alpha;
col*=vOpacity; ... }
|
如此,气泡效果就基本实现了,有几点可以自行优化:
- 将气泡动画和文字有机结合在一起
- 给气泡赋予随机的颜色
- 让文字变成可编辑的(提示:
contenteditable
)
最后
希望本文能给你创作新特效的灵感,keep creating~