Files
esengine/packages/procgen/src/noise/FBM.ts

215 lines
5.6 KiB
TypeScript
Raw Normal View History

/**
* @zh (FBM)
* @en Fractal Brownian Motion (FBM)
*
* @zh
* @en Creates more natural effects by layering multiple noise octaves
*/
/**
* @zh
* @en Noise function interface
*/
export interface INoise2D {
noise2D(x: number, y: number): number;
}
/**
* @zh (3D)
* @en Noise function interface (3D)
*/
export interface INoise3D {
noise3D(x: number, y: number, z: number): number;
}
/**
* @zh FBM
* @en FBM configuration
*/
export interface FBMConfig {
/**
* @zh
* @en Number of octaves (layers)
*/
octaves: number;
/**
* @zh
* @en Frequency multiplier per octave
*/
lacunarity: number;
/**
* @zh
* @en Amplitude decay per octave
*/
persistence: number;
/**
* @zh
* @en Initial frequency
*/
frequency: number;
/**
* @zh
* @en Initial amplitude
*/
amplitude: number;
}
const DEFAULT_CONFIG: FBMConfig = {
octaves: 6,
lacunarity: 2.0,
persistence: 0.5,
frequency: 1.0,
amplitude: 1.0
};
/**
* @zh FBM
* @en FBM noise generator
*/
export class FBM {
private readonly _noise: INoise2D & Partial<INoise3D>;
private readonly _config: FBMConfig;
/**
* @zh FBM
* @en Create FBM noise generator
*
* @param noise - @zh @en Base noise function
* @param config - @zh @en Configuration
*/
constructor(noise: INoise2D & Partial<INoise3D>, config?: Partial<FBMConfig>) {
this._noise = noise;
this._config = { ...DEFAULT_CONFIG, ...config };
}
/**
* @zh 2D FBM
* @en 2D FBM noise
*
* @param x - @zh X @en X coordinate
* @param y - @zh Y @en Y coordinate
* @returns @zh @en Noise value
*/
noise2D(x: number, y: number): number {
let value = 0;
let frequency = this._config.frequency;
let amplitude = this._config.amplitude;
let maxValue = 0;
for (let i = 0; i < this._config.octaves; i++) {
value += this._noise.noise2D(x * frequency, y * frequency) * amplitude;
maxValue += amplitude;
amplitude *= this._config.persistence;
frequency *= this._config.lacunarity;
}
return value / maxValue;
}
/**
* @zh 3D FBM
* @en 3D FBM noise
*
* @param x - @zh X @en X coordinate
* @param y - @zh Y @en Y coordinate
* @param z - @zh Z @en Z coordinate
* @returns @zh @en Noise value
*/
noise3D(x: number, y: number, z: number): number {
if (!this._noise.noise3D) {
throw new Error('Base noise does not support 3D');
}
let value = 0;
let frequency = this._config.frequency;
let amplitude = this._config.amplitude;
let maxValue = 0;
for (let i = 0; i < this._config.octaves; i++) {
value += this._noise.noise3D(x * frequency, y * frequency, z * frequency) * amplitude;
maxValue += amplitude;
amplitude *= this._config.persistence;
frequency *= this._config.lacunarity;
}
return value / maxValue;
}
/**
* @zh Ridged FBM
* @en Ridged FBM (suitable for mountains)
*/
ridged2D(x: number, y: number): number {
let value = 0;
let frequency = this._config.frequency;
let amplitude = this._config.amplitude;
let weight = 1;
for (let i = 0; i < this._config.octaves; i++) {
let signal = this._noise.noise2D(x * frequency, y * frequency);
signal = 1 - Math.abs(signal);
signal *= signal;
signal *= weight;
weight = Math.max(0, Math.min(1, signal * 2));
value += signal * amplitude;
frequency *= this._config.lacunarity;
amplitude *= this._config.persistence;
}
return value;
}
/**
* @zh Turbulence使
* @en Turbulence (using absolute value)
*/
turbulence2D(x: number, y: number): number {
let value = 0;
let frequency = this._config.frequency;
let amplitude = this._config.amplitude;
let maxValue = 0;
for (let i = 0; i < this._config.octaves; i++) {
value += Math.abs(this._noise.noise2D(x * frequency, y * frequency)) * amplitude;
maxValue += amplitude;
amplitude *= this._config.persistence;
frequency *= this._config.lacunarity;
}
return value / maxValue;
}
/**
* @zh Billowed
* @en Billowed (suitable for clouds)
*/
billowed2D(x: number, y: number): number {
let value = 0;
let frequency = this._config.frequency;
let amplitude = this._config.amplitude;
let maxValue = 0;
for (let i = 0; i < this._config.octaves; i++) {
const n = this._noise.noise2D(x * frequency, y * frequency);
value += (Math.abs(n) * 2 - 1) * amplitude;
maxValue += amplitude;
amplitude *= this._config.persistence;
frequency *= this._config.lacunarity;
}
return value / maxValue;
}
}
/**
* @zh FBM
* @en Create FBM noise generator
*/
export function createFBM(noise: INoise2D & Partial<INoise3D>, config?: Partial<FBMConfig>): FBM {
return new FBM(noise, config);
}