101 lines
3.1 KiB
TypeScript
101 lines
3.1 KiB
TypeScript
|
|
import type { Particle } from '../Particle';
|
|||
|
|
import type { IParticleModule } from './IParticleModule';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 值噪声哈希函数
|
|||
|
|
* Value noise hash function
|
|||
|
|
*
|
|||
|
|
* 使用经典的整数哈希算法生成伪随机值
|
|||
|
|
* Uses classic integer hash algorithm to generate pseudo-random values
|
|||
|
|
*/
|
|||
|
|
export function noiseHash(x: number, y: number): number {
|
|||
|
|
const n = x + y * 57;
|
|||
|
|
const shifted = (n << 13) ^ n;
|
|||
|
|
return ((shifted * (shifted * shifted * 15731 + 789221) + 1376312589) & 0x7fffffff) / 0x7fffffff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 2D 值噪声函数
|
|||
|
|
* 2D value noise function
|
|||
|
|
*
|
|||
|
|
* 基于双线性插值的简化值噪声实现,返回 [-1, 1] 范围的值
|
|||
|
|
* Simplified value noise using bilinear interpolation, returns value in [-1, 1] range
|
|||
|
|
*/
|
|||
|
|
export function valueNoise2D(x: number, y: number): number {
|
|||
|
|
const ix = Math.floor(x);
|
|||
|
|
const iy = Math.floor(y);
|
|||
|
|
const fx = x - ix;
|
|||
|
|
const fy = y - iy;
|
|||
|
|
|
|||
|
|
const n00 = noiseHash(ix, iy);
|
|||
|
|
const n10 = noiseHash(ix + 1, iy);
|
|||
|
|
const n01 = noiseHash(ix, iy + 1);
|
|||
|
|
const n11 = noiseHash(ix + 1, iy + 1);
|
|||
|
|
|
|||
|
|
// 双线性插值 | Bilinear interpolation
|
|||
|
|
const nx0 = n00 + (n10 - n00) * fx;
|
|||
|
|
const nx1 = n01 + (n11 - n01) * fx;
|
|||
|
|
return (nx0 + (nx1 - nx0) * fy) * 2 - 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 噪声模块 - 添加随机扰动
|
|||
|
|
* Noise module - adds random perturbation
|
|||
|
|
*/
|
|||
|
|
export class NoiseModule implements IParticleModule {
|
|||
|
|
readonly name = 'Noise';
|
|||
|
|
enabled = true;
|
|||
|
|
|
|||
|
|
/** 位置噪声强度 | Position noise strength */
|
|||
|
|
positionAmount: number = 0;
|
|||
|
|
|
|||
|
|
/** 速度噪声强度 | Velocity noise strength */
|
|||
|
|
velocityAmount: number = 0;
|
|||
|
|
|
|||
|
|
/** 旋转噪声强度 | Rotation noise strength */
|
|||
|
|
rotationAmount: number = 0;
|
|||
|
|
|
|||
|
|
/** 缩放噪声强度 | Scale noise strength */
|
|||
|
|
scaleAmount: number = 0;
|
|||
|
|
|
|||
|
|
/** 噪声频率 | Noise frequency */
|
|||
|
|
frequency: number = 1;
|
|||
|
|
|
|||
|
|
/** 噪声滚动速度 | Noise scroll speed */
|
|||
|
|
scrollSpeed: number = 1;
|
|||
|
|
|
|||
|
|
private _time: number = 0;
|
|||
|
|
|
|||
|
|
update(p: Particle, dt: number, _normalizedAge: number): void {
|
|||
|
|
this._time += dt * this.scrollSpeed;
|
|||
|
|
|
|||
|
|
// 基于粒子位置和时间的噪声 | Noise based on particle position and time
|
|||
|
|
const noiseX = valueNoise2D(p.x * this.frequency + this._time, p.y * this.frequency);
|
|||
|
|
const noiseY = valueNoise2D(p.x * this.frequency, p.y * this.frequency + this._time);
|
|||
|
|
|
|||
|
|
// 位置噪声 | Position noise
|
|||
|
|
if (this.positionAmount !== 0) {
|
|||
|
|
p.x += noiseX * this.positionAmount * dt;
|
|||
|
|
p.y += noiseY * this.positionAmount * dt;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 速度噪声 | Velocity noise
|
|||
|
|
if (this.velocityAmount !== 0) {
|
|||
|
|
p.vx += noiseX * this.velocityAmount * dt;
|
|||
|
|
p.vy += noiseY * this.velocityAmount * dt;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 旋转噪声 | Rotation noise
|
|||
|
|
if (this.rotationAmount !== 0) {
|
|||
|
|
p.rotation += noiseX * this.rotationAmount * dt;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 缩放噪声 | Scale noise
|
|||
|
|
if (this.scaleAmount !== 0) {
|
|||
|
|
const scaleDelta = noiseX * this.scaleAmount * dt;
|
|||
|
|
p.scaleX += scaleDelta;
|
|||
|
|
p.scaleY += scaleDelta;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|