3、粒子纹理:加载纹理贴图、圆形渐变纹理、自定义纹理制作
纹理这东西,说白了就是粒子的「皮肤」。没有纹理的粒子,就像没穿衣服的小人——能看,但总觉得少了点什么。我个人习惯把纹理看作是粒子系统的灵魂,选对了纹理,整个效果立马就活了。
今天咱们就聊聊三种常用的纹理加载方式:直接加载图片、用 Canvas 生成圆形渐变、以及完全自定义纹理。嗯,这三种方式我在项目里都踩过坑,一个一个说。
3.1 加载纹理贴图
最直接的方式,就是用 TextureLoader 加载一张现成的图片。比如星星、光晕、雪花,网上找一张 PNG 透明底图就行。
const loader = new THREE.TextureLoader();
const texture = loader.load('path/to/star.png');
const material = new THREE.PointsMaterial({
size: 0.5,
map: texture,
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false
});
这里有个细节:transparent: true 必须开,否则透明背景会变成白色方块。我刚开始做的时候忘了这茬,结果满屏白点,还以为是图片坏了……
另外,AdditiveBlending 是粒子特效的标配。它会让重叠的粒子颜色叠加变亮,产生发光效果。你想想看,星星、火花、烟雾,哪个不是越叠越亮?
3.2 圆形渐变纹理
很多时候我们不需要外部的图片,用 Canvas 画一个圆形渐变纹理就够了。这样做的好处是:不依赖外部资源,加载快,而且可以动态调整。
function createCircleTexture() {
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');
const gradient = ctx.createRadialGradient(32, 32, 0, 32, 32, 32);
gradient.addColorStop(0, 'rgba(255,255,255,1)');
gradient.addColorStop(0.5, 'rgba(255,255,255,0.8)');
gradient.addColorStop(1, 'rgba(255,255,255,0)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 64, 64);
return new THREE.CanvasTexture(canvas);
}
const texture = createCircleTexture();
这段代码的核心是 createRadialGradient。从圆心到边缘,颜色从白到透明渐变。边缘羽化得越柔和,粒子看起来就越自然。
addColorStop(0.5, ...) 改成 addColorStop(0.8, ...)。反之,想要更柔和,就把 0.5 改成 0.2。我在做星云效果时,就喜欢用 0.3 左右的渐变,看起来像一团朦胧的气体。
为什么会用 Canvas 而不是直接加载图片?说白了,Canvas 纹理是「活」的。你可以在运行时修改渐变参数,生成不同大小、不同透明度的纹理。这在动态粒子系统里特别有用。
3.3 自定义纹理制作
圆形渐变虽然好用,但有时候我们需要更复杂的纹理。比如带尾巴的彗星、带棱角的雪花、或者不规则的光斑。这时候就得自己动手画了。
我记得有一次做粒子爆炸特效,需要每个粒子在爆炸瞬间有一个「拖尾」效果。圆形渐变显然不行,得画一个长条形的纹理,一端亮一端暗。
function createCustomTexture() {
const canvas = document.createElement('canvas');
canvas.width = 128;
canvas.height = 128;
const ctx = canvas.getContext('2d');
// 画一个带尾巴的圆形
ctx.beginPath();
ctx.arc(64, 64, 20, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
// 画尾巴
ctx.beginPath();
ctx.moveTo(64, 64);
ctx.lineTo(120, 80);
ctx.lineTo(110, 100);
ctx.closePath();
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fill();
return new THREE.CanvasTexture(canvas);
}
你看,这里我画了一个圆,然后在右边加了一个半透明的三角形尾巴。实际渲染时,粒子会带着这个尾巴旋转,看起来就像在运动。
嗯,这里要注意一点:Canvas 纹理的尺寸不要太大。128x128 已经够用了,再大就浪费 GPU 带宽。我曾经为了追求细节,画了 512x512 的纹理,结果粒子数量一多,帧率直接掉到 20。后来改成 64x64,效果几乎没差别,帧率却回到了 60。
3.4 三种纹理的对比
| 类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 加载图片 | 效果丰富,可直接用设计稿 | 依赖外部资源,加载慢 | 固定效果的粒子(如雪花、星星) |
| 圆形渐变 | 轻量、无外部依赖、可动态调整 | 形状单一,只有圆形 | 光晕、烟雾、通用粒子 |
| 自定义纹理 | 形状任意,灵活度高 | 需要手写 Canvas 代码 | 特殊效果(拖尾、箭头、不规则形状) |
我个人建议:能用圆形渐变就别用图片,能用自定义就别用圆形渐变。说白了,越「轻」的方案,性能越好,维护成本也越低。但如果你需要特别复杂的纹理,比如带渐变色的星云图案,那还是老老实实加载图片吧。
好了,纹理这块就聊这么多。下一节咱们会把这些纹理真正用到粒子系统里,看看它们在不同场景下的表现。你想想看,同样的粒子,换上不同的纹理,效果能差出十万八千里——这就是纹理的魅力。