Files
esengine/packages/procgen/src/noise/FBM.ts
YHH 275124b66c feat(procgen): 添加程序化生成工具包 (#331)
- 添加噪声函数 (Perlin, Simplex, Worley, FBM)
- 添加种子随机数生成器 (SeededRandom)
- 添加加权随机选择和洗牌工具
- 添加蓝图节点 (SampleNoise2D, SeededRandom, WeightedPick 等)
2025-12-25 14:33:19 +08:00

215 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @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);
}