231 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			231 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 \file Random.cpp
 | 
						|
 
 | 
						|
 \maintainer Morgan McGuire, http://graphics.cs.williams.edu
 | 
						|
 
 | 
						|
 \created 2009-01-02
 | 
						|
 \edited  2012-03-29
 | 
						|
 | 
						|
 Copyright 2000-2012, Morgan McGuire.
 | 
						|
 All rights reserved.
 | 
						|
 */
 | 
						|
#include "G3D/Random.h"
 | 
						|
 | 
						|
namespace G3D {
 | 
						|
 | 
						|
Random& Random::common() {
 | 
						|
    static Random r;
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Random::Random(void* x) : state(NULL), m_threadsafe(false) {
 | 
						|
    (void)x;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Random::Random(uint32 seed, bool threadsafe) : m_threadsafe(threadsafe) {
 | 
						|
    state = new uint32[N];
 | 
						|
    reset(seed, threadsafe);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Random::reset(uint32 seed, bool threadsafe) {
 | 
						|
    m_threadsafe = threadsafe;
 | 
						|
    
 | 
						|
    const uint32 X = 1812433253UL;
 | 
						|
 | 
						|
    state[0] = seed;
 | 
						|
    for (index = 1; index < (int)N; ++index) {
 | 
						|
        state[index] = X * (state[index - 1] ^ (state[index - 1] >> 30)) + index;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Random::~Random() {
 | 
						|
    delete[] state;
 | 
						|
    state = NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
uint32 Random::bits() {
 | 
						|
    // See http://en.wikipedia.org/wiki/Mersenne_twister
 | 
						|
 | 
						|
    // Make a local copy of the index variable to ensure that it
 | 
						|
    // is not out of bounds
 | 
						|
    int localIndex = index;
 | 
						|
 | 
						|
    // Automatically checks for index < 0 if corrupted
 | 
						|
    // by unsynchronized threads.
 | 
						|
    if ((unsigned int)localIndex >= (unsigned int)N) {
 | 
						|
        generate();
 | 
						|
        localIndex = 0;
 | 
						|
    }
 | 
						|
    // Increment the global index.  It may go out of bounds on
 | 
						|
    // multiple threads, but the above check ensures that the
 | 
						|
    // array index actually used never goes out of bounds.
 | 
						|
    // It doesn't matter if we grab the same array index twice
 | 
						|
    // on two threads, since the distribution of random numbers
 | 
						|
    // will still be uniform.
 | 
						|
    ++index;
 | 
						|
    // Return the next random in the sequence
 | 
						|
    uint32 r = state[localIndex];
 | 
						|
 | 
						|
    // Temper the result
 | 
						|
    r ^=  r >> U;
 | 
						|
    r ^= (r << S) & B;
 | 
						|
    r ^= (r << T) & C;
 | 
						|
    r ^=  r >> L;
 | 
						|
    
 | 
						|
    return r;    
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/** Generate the next N ints, and store them for readback later */
 | 
						|
void Random::generate() {
 | 
						|
    // Lower R bits
 | 
						|
    static const uint32 LOWER_MASK = (1LU << R) - 1;
 | 
						|
 | 
						|
    // Upper (32 - R) bits
 | 
						|
    static const uint32 UPPER_MASK = 0xFFFFFFFF << R;
 | 
						|
    static const uint32 mag01[2] = {0UL, (uint32)A};
 | 
						|
 | 
						|
    if (m_threadsafe) {
 | 
						|
        bool contention = ! lock.lock();
 | 
						|
        if (contention)  {
 | 
						|
            // Another thread just generated a set of numbers; no need for
 | 
						|
            // this thread to do it too
 | 
						|
            lock.unlock();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // First N - M
 | 
						|
    for (unsigned int i = 0; i < N - M; ++i) {    
 | 
						|
        uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
 | 
						|
        state[i] = state[i + M] ^ (x >> 1) ^ mag01[x & 1];
 | 
						|
    }
 | 
						|
 | 
						|
    // Rest
 | 
						|
    for (unsigned int i = N - M + 1; i < N - 1; ++i) {    
 | 
						|
        uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
 | 
						|
        state[i] = state[i + (M - N)] ^ (x >> 1) ^ mag01[x & 1];
 | 
						|
    }
 | 
						|
        
 | 
						|
    uint32 y = (state[N - 1] & UPPER_MASK) | (state[0] & LOWER_MASK);
 | 
						|
    state[N - 1] = state[M - 1] ^ (y >> 1) ^ mag01[y & 1];
 | 
						|
    index = 0;
 | 
						|
 | 
						|
    if (m_threadsafe) {
 | 
						|
        lock.unlock();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
    
 | 
						|
int Random::integer(int low, int high) {
 | 
						|
    debugAssert(high >= low);
 | 
						|
    int r = iFloor(low + (high - low + 1) * (double)bits() / 0xFFFFFFFFUL);
 | 
						|
 | 
						|
    // There is a *very small* chance of generating
 | 
						|
    // a number larger than high.
 | 
						|
    if (r > high) {
 | 
						|
        return high;
 | 
						|
    } else {
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
    
 | 
						|
float Random::gaussian(float mean, float stdev) {
 | 
						|
 | 
						|
    // Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html
 | 
						|
    // Modified to specify standard deviation and mean of distribution
 | 
						|
    float w, x1, x2;
 | 
						|
 | 
						|
    // Loop until w is less than 1 so that log(w) is negative
 | 
						|
    do {
 | 
						|
        x1 = uniform(-1.0, 1.0);
 | 
						|
        x2 = uniform(-1.0, 1.0);
 | 
						|
 | 
						|
        w = float(square(x1) + square(x2));
 | 
						|
    } while (w > 1.0f);
 | 
						|
 | 
						|
    // Transform to gassian distribution
 | 
						|
    // Multiply by sigma (stdev ^ 2) and add mean.
 | 
						|
    return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean; 
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Random::cosSphere(float& x, float& y, float& z) {
 | 
						|
    cosHemi(x, y, z);
 | 
						|
    if (bits() & 1) {
 | 
						|
        // Choose the axis direction uniformly at random
 | 
						|
        z = -z;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Random::cosHemi(float& x, float& y, float& z) {
 | 
						|
    const float e1 = uniform();
 | 
						|
    const float e2 = uniform();
 | 
						|
 | 
						|
    // Jensen's method 
 | 
						|
    const float sin_theta = sqrtf(1.0f - e1);
 | 
						|
    const float cos_theta = sqrtf(e1);
 | 
						|
    const float phi = 6.28318531f * e2;
 | 
						|
 | 
						|
    x = cos(phi) * sin_theta;
 | 
						|
    y = sin(phi) * sin_theta;
 | 
						|
    z = cos_theta;
 | 
						|
 | 
						|
    // We could also use Malley's method (pbrt p.657), since they are the same cost:
 | 
						|
    //
 | 
						|
    //  r = sqrt(e1);
 | 
						|
    //  t = 2*pi*e2;
 | 
						|
    //  x = cos(t)*r;
 | 
						|
    //  y = sin(t)*r;
 | 
						|
    //  z = sqrt(1.0 - x*x + y*y);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Random::cosPowHemi(const float k, float& x, float& y, float& z) {
 | 
						|
    const float e1 = uniform();
 | 
						|
    const float e2 = uniform();
 | 
						|
 | 
						|
    const float cos_theta = pow(e1, 1.0f / (k + 1.0f));
 | 
						|
    const float sin_theta = sqrtf(1.0f - square(cos_theta));
 | 
						|
    const float phi = 6.28318531f * e2;
 | 
						|
 | 
						|
    x = cos(phi) * sin_theta;
 | 
						|
    y = sin(phi) * sin_theta;
 | 
						|
    z = cos_theta;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Random::hemi(float& x, float& y, float& z) {
 | 
						|
    sphere(x, y, z);
 | 
						|
    z = fabsf(z);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Random::sphere(float& x, float& y, float& z) {
 | 
						|
    // Squared magnitude
 | 
						|
    float m2;
 | 
						|
 | 
						|
    // Rejection sample
 | 
						|
    do {
 | 
						|
        x = uniform() * 2.0f - 1.0f, 
 | 
						|
        y = uniform() * 2.0f - 1.0f,
 | 
						|
        z = uniform() * 2.0f - 1.0f;
 | 
						|
        m2 = x*x + y*y + z*z;
 | 
						|
    } while (m2 >= 1.0f);
 | 
						|
 | 
						|
    // Divide by magnitude to produce a unit vector
 | 
						|
    float s = rsqrt(m2);
 | 
						|
    x *= s;
 | 
						|
    y *= s;
 | 
						|
    z *= s;
 | 
						|
}
 | 
						|
 | 
						|
} // G3D
 |