Leveraging Beta Distributions For Flexible Random Gradients
Using Beta Distributions for Dynamic Randomness
Beta distributions are continuous probability distributions defined over the interval [0, 1] parameterized by two positive shape parameters, commonly labeled α and β. Unlike the uniform distribution which assigns equal probability over the [0, 1] range, beta distributions allow for a variety of distribution shapes based on the choice of the α and β parameters.
Some properties of beta distributions that make them useful for procedural content generation include:
- Flexibility – Different choices of α and β lead to different distribution shapes like uniform, triangular, normal, and more.
- Bounded range – Always defined over [0, 1], convenient formodeling percentages and random gradients.
- Conjugate prior – Useful for Bayesian analysis when the data is assumed beta-distributed.
- Defined for positive and negative α and β – Allows skewed J shaped and mirrored J shaped distributions.
By providing control over the shape of the randomness, beta distributions have benefits over default uniform distributions for tasks like adding aesthetic variety, tuning challenge difficulty, or creating natural-looking outputs. The α and β parameters directly map to controlling the form of the distribution.
Controlling the Shape with Alpha and Beta Parameters
The α and β parameters have an intuitive effect on the distribution shape. α mainly controls the height of the peak while β controls how fast the distribution drops off. Some examples:
- α = β = 1 is the standard uniform distribution
- α < 1, β = 1 is a J shaped distribution clustered near 0
- α = 1, β < 1 is clustered near 1
- α = β > 1 is a distribution clustered in the middle peak
- α < β results in negative skew clustered on the high end
- α > β results in positive skew clustered on the low end
Code to sample from a beta distribution is simple using common math libraries. For example with Python and NumPy:
import numpy as np # Sample with a=2, b=5 samples = np.random.beta(2, 5, size=100)
By tuning the parameters, beta distributions can generate randomness matched to your use case, whether a softly tapering gradient or clusters of sharp spikes.
Fitting Gradient Noise to Your Game
Dynamic randomness like Perlin noise is commonly used in games for effects animation, environment texturing, and procedural content generation. Beta distributions provide a flexible way to control gradient randomness for enhanced variety.
Noise for Aesthetic Effects
Gradient noise mapped to color channels, transform properties, or shader parameters can create flowing heat shimmer, flickering lights, wavering dimensions, pulsing power-ups, and watery refractions. Beta distributions lend organic-feeling variance by clustering parameter changes only slightly away from defaults rather than uniformly across extremes.
float betaSample = SampleBetaNoise(2.0, 5.0); // Map to 0-1 color gradient color = lerp(darkColor, lightColor, betaSample);
Noise for Gameplay Variety
Procedural generation via noise creates unpredictable level geography, enemy spawn locations, loot drop quantities, and character stats. Beta distribution driven noise stops extremes like excessively sparse or dense enemy counts. This maintains enjoyable challenge without too much randomness.
int numEnemies = Round(SampleBetaNoise(1.5, 3) * maxEnemies);
Optimizing Challenge with Noise Parameters
The α and β allow explicit control over the noise profile used for generation. Increasing β relative to α concentrates magnitude towards average values. This tunes content to a softly varying sweet spot rather than frequently hitting extremes. Lower α biases downwards and higher biases upwards for asymmetric effects.
// Cluster nearer to low quantities int numCoins = Round(SampleBetaNoise(1, 4) * maxCoins);
Sample Code for Gradient Noise Functions
Beta distribution noise can be implemented efficiently in code by sampling the standard library beta distribution. Wrapping with Perlin ridged noise gives the standard flowing gradient effect.
float SampleBetaNoise(float alpha, float beta) { // Perlin ridged noise from -1 to 1 float perlin = PerlinRidged2D(uv); // Map Perlin value to beta distribution parameters float a = Remap(perlin, -1, 1, alpha*0.5, alpha*2); float b = Remap(perlin, -1, 1, beta*0.5, beta*2); return SampleBetaDistribution(a, b); }
The flexible beta distribution integrates well into procedural pipelines for aesthetic and functional randomness.
Advanced Usages and Special Cases
While the standard univariate form covers most use cases, beta distributions also support multivariate, parameterized, and data-fit versions for special needs.
Correlated Multivariate Betas
Multivariate beta distributions allow encoding correlations between multiple random dimensions. This maintains consistent relationships in the noise. For instance terrain elevation could modulate settlement density or temperature shifts might alter weapon spread.
// Correlated samples float[2] sample = MultivariateBeta([a1, a2], [b1, b2], Rho); // Terrain and structures float elevation = sample[0]; float density = sample[1];
Beta Distributions in Shader Graphs
The Unity Shader Graph allows rapid visual prototyping of graphics effects using node workflows. Beta distribution nodes offer enhanced control compared to default uniform noise.
// Flowing beta gradient mapped to color float alpha = RandomRange(0.5, 2); float beta = RandomRange(3, 6); float t = BetaDistribution(uv.rg, alpha, beta); color = GradientLerp(t);
Issues with Extreme Parameter Values
Beta distribution sampling can encounter numerical instability for very small α < 0.05 or very large α, β > 250 parameter values due to floating point precision limits. Workarounds include clamping parameters or directly computing the probability density function.
float sample = ClampedBeta(a, b); float pdf = BetaPDF(x, a, b); float cdf = BetaCDF(x, a, b) float sample = BetaCDFInverse(cdf, a, b);
Example Code for Advanced Beta Usage
This reusable BetaDistribution class encapsulates best practices for robust sampling and handles edge case parameter values.
public class BetaDistribution { public float Sample(float alpha, float beta) { alpha = Mathf.Clamp(alpha, 0.05, 250); beta = Mathf.Clamp(beta, 0.05, 250); if (alpha < 0.1 || beta < 0.1) { return SampleCDF(alpha, beta); } else { return Math.Beta(alpha, beta); } } private float SampleCDF(float alpha, float beta) { // Implement CDF sampling } }
With smart parameter selection and error handling, beta distributions enable advanced procedural effects.
Conclusion
Summary of Techniques
Beta distributions' flexibility through the α and β parameters gives improved control over random gradients compared to plain uniform noise. Sample uses:
- Aesthetic variety - flowing colors, animation offsets
- Tuned challenge - enemy counts, loot drops
- Organic feel - clustered nearer to defaults
- Correlated dimensions - linking environment factors
- Shader graphs - unlocked new noise effects
Links to Other Procedural Generation Methods
Beta noise combines well with other common procedural techniques like Perlin and cellular noise, diffusion limited aggregation, and Markov chains for powering entire generative pipelines.
Invitation for Reader Suggestions and Feedback
Does your team have clever uses for beta distribution noise? What other distribution shapes would be useful? Share your experiences in the comments below!