62 lines
2.8 KiB
C#
62 lines
2.8 KiB
C#
// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
|
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
|
|
|
// Random numbers which also allow creation of random values in the shaders and are deterministic
|
|
// Based on this article:
|
|
// http://martindevans.me/game-development/2015/02/22/Random-Gibberish/
|
|
|
|
using System;
|
|
|
|
namespace math
|
|
{
|
|
/// <summary>
|
|
/// The <see cref="RandomSeed"/> is a structure for deterministically acquiring random values.
|
|
/// One <see cref="RandomSeed"/> should be able to reproduce the same pseudo-random value for a fixed offset, but
|
|
/// provide enough random distribution for different offsets or different random seeds
|
|
/// Although other methods exist, the current implementation can easily be replicated in the shaders if required
|
|
/// </summary>
|
|
public struct RandomSeed
|
|
{
|
|
private const double GelfondConst = 23.1406926327792690; // e to the power of Pi = (-1) to the power of -i
|
|
private const double GelfondSchneiderConst = 2.6651441426902251; // 2 to the power of sqrt(2)
|
|
private const double Numerator = 123456789;
|
|
|
|
// When casting uint to double it works fine, but when casting it to float it might cause underflow errors (loss of precision)
|
|
// We want to limit the maximum settable value to prevent such errors.
|
|
private const uint UnderflowGuard = 0xFFFF;
|
|
|
|
private readonly uint seed;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="RandomSeed"/> struct from a target uint.
|
|
/// </summary>
|
|
/// <param name="seed">The seed value to initialize the deterministic random generator.</param>
|
|
public RandomSeed( uint seed )
|
|
{
|
|
this.seed = ( seed & UnderflowGuard );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a deterministic double value between 0 and 1 based on the seed
|
|
/// </summary>
|
|
/// <returns>Deterministic pseudo-random value between 0 and 1</returns>
|
|
public double GetDouble( uint offset )
|
|
{
|
|
var dRand = (double)( unchecked(seed + offset) ); // We want it to overflow
|
|
|
|
var dotProduct = Math.Cos( dRand ) * GelfondConst + Math.Sin( dRand ) * GelfondSchneiderConst;
|
|
var denominator = 1e-7 + 256 * dotProduct;
|
|
var remainder = Numerator % denominator;
|
|
|
|
return ( remainder - Math.Floor( remainder ) );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a deterministic float value between 0 and 1 based on the seed
|
|
/// The calculations are still made as doubles to prevent underflow errors.
|
|
/// </summary>
|
|
/// <returns>Deterministic pseudo-random value between 0 and 1</returns>
|
|
public float GetFloat( uint offset ) => (float)GetDouble( offset );
|
|
}
|
|
}
|