using UnityEngine; using System.Collections; namespace Gaia { /// /// Fractal generator /// public class FractalGenerator { /// /// The types of fractals we support /// public enum Fractals { Perlin, Billow, RidgeMulti }; /// /// Seed - different seeds generate replicable different fractals /// private float m_seed = 0; public float Seed { get { return m_seed; } set { m_seed = value; } } /// /// The amount of detail in the fractal - more octaves mean more detail and longer calc time. /// private int m_octaves = 8; public int Octaves { get { return m_octaves; } set { m_octaves = value; } } /// /// The roughness of the fractal noise. Controls how quickly amplitudes diminish for successive octaves. 0..1. /// private float m_persistence = 0.65f; public float Persistence { get { return m_persistence; } set { m_persistence = value; } } /// /// The frequency of the first octave /// private float m_frequency = 1f; public float Frequency { get { return m_frequency; } set { m_frequency = value; } } /// /// The frequency multiplier between successive octaves. Experiment between 1.5 - 3.5. /// private float m_lacunarity = 1.5f; public float Lacunarity { get { return m_lacunarity; } set { m_lacunarity = value; } } /// /// Offset X by a given value, allows local fractal area to be explored. /// private float m_XOffset = 0f; public float XOffset { get { return m_XOffset; } set { m_XOffset = value; } } /// /// Offset height by a given value. Decease to get islands, increease to get plateaus. /// private float m_ZOffset = 0f; public float ZOffset { get { return m_ZOffset; } set { m_ZOffset = value; } } /// /// Offset Y by a given value, allows local fractal area to be explored. /// private float m_YOffset = 0f; public float YOffset { get { return m_YOffset; } set { m_YOffset = value; } } /// /// The type of fractal we are generating /// private Fractals m_fractalType = Fractals.Perlin; public Fractals FractalType { get { return m_fractalType; } set { m_fractalType = value; switch (m_fractalType) { case Fractals.Perlin: { m_noiseCalculator = GetValue_Perlin; break; } case Fractals.Billow: { m_noiseCalculator = GetValue_Billow; break; } case Fractals.RidgeMulti: { CalcSpectralWeights(); m_noiseCalculator = GetValue_RidgedMulti; break; } } } } private float[] m_spectralWeights = new float[20]; //Used by ridged fractal private delegate float GetCalcValue(float x, float z); //Switch in the relevant fractal algorithm private GetCalcValue m_noiseCalculator; //Switch in the relevant fractal algorithm /// /// Constructor /// public FractalGenerator() { FractalType = Fractals.Perlin; } /// /// Construct and initialise a fractal generator /// /// The frequency of the first octave /// The frequency multiplier between successive octaves. Experiment between 1.5 - 3.5. /// The amount of detail in the fractal - more octaves mean more detail and longer calc time. /// The roughness of the fractal noise. Controls how quickly amplitudes diminish for successive octaves. 0..1. /// Seed - different seeds generate replicable different fractals /// The type of generator being used public FractalGenerator(float frequency, float lacunarity, int octaves, float persistance, float seed, Fractals type) { m_frequency = frequency; m_lacunarity = lacunarity; m_octaves = octaves; m_persistence = persistance; m_seed = seed; switch (type) { case Fractals.Perlin: m_noiseCalculator = GetValue_Perlin; break; case Fractals.Billow: m_noiseCalculator = GetValue_Billow; break; case Fractals.RidgeMulti: CalcSpectralWeights(); m_noiseCalculator = GetValue_RidgedMulti; break; default: m_noiseCalculator = GetValue_Perlin; break; } } /// /// Set some reasonable defaults for the fractal type /// public void SetDefaults() { switch (m_fractalType) { case Fractals.Perlin: { m_frequency = 1f; m_lacunarity = 2f; m_octaves = 6; m_persistence = 0.5f; m_seed = 0; m_noiseCalculator = GetValue_Perlin; break; } case Fractals.Billow: { m_frequency = 1f; m_lacunarity = 2f; m_octaves = 6; m_persistence = 0.5f; m_seed = 0; m_noiseCalculator = GetValue_Billow; break; } case Fractals.RidgeMulti: { m_frequency = 1f; m_lacunarity = 2f; m_octaves = 6; m_seed = 0; CalcSpectralWeights(); m_noiseCalculator = GetValue_RidgedMulti; break; } } } /// /// Get the noise value at this point /// /// x location /// z location /// public float GetValue(float x, float z) { return m_noiseCalculator(x, z); } /// /// Get the noise value at this point /// /// x location /// z location /// Value at this point public double GetValue(double x, double z) { return GetValue((float)x, (float)z); } /// /// Get the noise value at this point /// /// x location /// z location /// Value in range 0..1 at this point public float GetNormalisedValue(float x, float z) { return Mathf.Clamp01((GetValue(x, z) + 1f) / 2f); } /// /// Get the noise value at this point /// /// x location /// z location /// Value in range 0..1 at this point public double GetNormalisedValue(double x, double z) { return GetNormalisedValue((float)x, (float)z); } /// /// Calculate a perlin fractal /// /// x location /// z location /// public float GetValue_Perlin(float x, float z) { float value = 0f; float signal = 0f; float persistence = 1f; float nx, nz; x += m_seed; z += m_seed; x += m_XOffset; z += m_ZOffset; x *= m_frequency; z *= m_frequency; for (int octave = 0; octave < m_octaves; octave++) { nx = x; nz = z; signal = SimplexNoiseGenerator.Generate(nx, nz); value += signal * persistence; x *= m_lacunarity; z *= m_lacunarity; persistence *= m_persistence; } value += m_YOffset * 2.4f; return value; } /// /// Calculate a billow fractal /// /// x location /// z location /// public float GetValue_Billow(float x, float z) { float value = 0f; float signal = 0f; float persistence = 1f; float nx, nz; x += m_seed; z += m_seed; x += m_XOffset; z += m_ZOffset; x *= m_frequency; z *= m_frequency; for (int octave = 0; octave < m_octaves; octave++) { nx = x; nz = z; signal = SimplexNoiseGenerator.Generate(nx, nz); signal = 2f * Mathf.Abs(signal) - 1f; value += signal * persistence; x *= m_lacunarity; z *= m_lacunarity; persistence *= m_persistence; } value += m_YOffset * 2.4f; return value; } /// /// Calculate a ridged multi fractal /// /// x location /// z location /// public float GetValue_RidgedMulti(float x, float z) { float signal = 0.0f; float value = 0.0f; float weight = 1.0f; float offset = 1f; float gain = m_persistence; float nx, nz; x += m_seed; z += m_seed; x += m_XOffset; z += m_ZOffset; x *= m_frequency; z *= m_frequency; for (int octave = 0; octave < m_octaves; octave++) { nx = x; nz = z; //Get the coherent-noise value from input value and add it to final result signal = SimplexNoiseGenerator.Generate(nx, nz); //Make the ridges signal = Mathf.Abs(signal); signal = offset - signal; //Square signal to increase sharpness of ridges. signal *= signal; //The weighting from previous octave is applied to the signal. signal *= weight; //Weight successive contributions by previous signal. weight = signal * gain; if (weight > 1.0) { weight = 1.0f; } if (weight < 0.0f) { weight = 0.0f; } //Add the signal to output value. value += signal * m_spectralWeights[octave]; //Next Octave x *= m_lacunarity; z *= m_lacunarity; } value = (value * 1.25f) - 1.0f; value += m_YOffset; return value; } /// /// Calculate spectral weights for the ridged fractal /// private void CalcSpectralWeights() { float h = 1.0f; float frequency = 1.0f; int maxSpectra = m_spectralWeights.GetLength(0); for (int i = 0; i < maxSpectra; i++) { m_spectralWeights[i] = Mathf.Pow(frequency, -h); frequency *= m_lacunarity; } } } }