分享一个酷炫的发光特效效果,可见在线案例。
该特效可以分成六个组成部分,一个转动的熔浆球体,一层透明气场,外发光,飞线聚集特效,内部发光,蓝色耀斑。
岩浆球体和透明气场是用几何球体实现,通过纹理移动(UV动画)来表达流动的岩浆和浮动的气场。
const loader = new THREE.TextureLoader();
const map = loader.load("texture.png");
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(2, 20, 20),
new THREE.MeshBasicMaterial({ map })
);
接下来对纹理添加设置,使纹理能够在做uv动画时重复贴图。然后每帧去移动纹理。
// 对纹理设置为垂直和水平方向重复
map.wrapS = map.wrapT = THREE.RepeatWrapping;
// performance.now() 是毫秒数
map.offset.x = performance.now() / 1000 / 2;
map.offset.y = performance.now() / 1000 / 2.5;
外发光实际上是一个有纹理的广告牌,始终面向相机。在 Three.js 中实现 billboard 是通过THREE.Sprite来实现。
// 加载纹理
const loader = new THREE.TextureLoader();
const map = loader.load("画像フォルダー/テクスチャー画像.png"); // 仮
const material = new THREE.SpriteMaterial({
map: map,
color: 0xffffff,
blending: THREE.AdditiveBlending,
transparent: true
});
const sprite = new THREE.Sprite(material);
// 默认尺寸比较小,调整合适的大小
sprite.scale.multiplyScalar(11);
// 加载纹理
const loader = new THREE.TextureLoader();
const map = loader.load("b.png"); // 仮
// 创建一个条形平面
const mesh = new Mesh(
new THREE.PlaneGeometry(0.04, 2),
new THREE.MeshBasicMaterial({ map });
);
提前将条形的Y坐标移至正,添加一个向中心点(0, 0)移动的动画;再添加一个透明度向中心点淡出的动画。然后在每一帧的函数添加处理。
// 每帧一点一点地移动,并使其更接近透明
mesh.position.y -= 0.5;
mesh.material.opacity -= 0.05;
// 如果透明度小于0,则初始化位置和透明度
if (mesh.material.opacity <= 0) {
mesh.position.y = 5;
mesh.material.opacity = 1;
}
这只是一个条形的处理。然后复制多个即可。
内部发光是在球体Mesh的shader上添加渐变来表达。顶点法线向量和相机位置向量夹角越小,就变得越透明。为此,我们计算顶点法线向量和相机位置向量的点积,并将该值用作透明度。
/**
* 顶点着色器
*/
uniform vec3 viewVector; // 相机位置
varying float opacity; // 传递给片元着色器的透明度
void main()
{
// 对顶点法向量归一化
vec3 nNomal = normalize(normal);
// 对相机位置向量归一化
vec3 nViewVec = normalize(viewVector);
// 两个向量的点积作为透明度
opacity = dot(nNomal, nViewVec);
// 反转
opacity = 1 - opacity;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
耀斑特效可以用多个环形几何Mesh来表示。我们可以为圆环Mesh添加 UV 动画和透明渐变,而边缘的透明渐变是为了防止看起来不自然。
这个时候片元着色器中的处理就很重要了,下面的代码是用于表示单个耀斑特效的片元着色器。
uniform sampler2D map; // 纹理数据
uniform float opacity; // 透明度
uniform float topRadius; // 外径
uniform float bottomRadius; // 内径
varying vec2 vUv; // UV坐标
varying float radius; // 顶点位置到中心的距离
const float PI = 3.1415926;
void main() {
// 从 uv 位置信息获取纹理颜色
vec4 tColor = texture2D(map, vUv);
// 环形边宽 = 圆柱顶半径 - 圆柱底半径
float width = topRadius - bottomRadius;
// 绘图位置占圆环宽度的百分比
float ratio = (radius - bottomRadius) / width;
float opacity = opacity * sin(PI * ratio);
// 基础颜色
vec4 baseColor = (tColor + vec4(0.0, 0.0, 0.3, 1.0));
// 反映透明度
gl_FragColor = baseColor * vec4(1.0, 1.0, 1.0, opacity);
}
之后,在顶点着色器中添加移动 UV 坐标的处理并执行 UV 动画,从而完成单个耀斑效果。
通过以与飞线特效相同的方式创建多个耀斑并赋予它们随机旋转的变量,就可以创建如下所示的耀斑特效。