126 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  @file PrecomputedRandom.cpp
 | |
|  
 | |
|  @maintainer Morgan McGuire, http://graphics.cs.williams.edu
 | |
|  
 | |
|  @created 2009-03-31
 | |
|  @edited  2009-07-01
 | |
| 
 | |
|  Copyright 2000-2009, Morgan McGuire.
 | |
|  All rights reserved.
 | |
|  */
 | |
| 
 | |
| #include "G3D/PrecomputedRandom.h"
 | |
| #include "G3D/System.h"
 | |
| 
 | |
| namespace G3D {
 | |
| 
 | |
| PrecomputedRandom::PrecomputedRandom(int dataSize, uint32 seed) : 
 | |
|  Random((void*)NULL),
 | |
|  m_hemiUniform(NULL),
 | |
|  m_sphereBits(NULL),
 | |
|  m_modMask(dataSize - 1),
 | |
|  m_freeData(true) {
 | |
| 
 | |
|     alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
 | |
|     m_index = seed & m_modMask;
 | |
| 
 | |
|     HemiUniformData* h;
 | |
|     SphereBitsData* s;
 | |
|     m_hemiUniform = h = (HemiUniformData*) System::malloc(sizeof(HemiUniformData) * dataSize);
 | |
|     m_sphereBits = s = (SphereBitsData*) System::malloc(sizeof(SphereBitsData) * dataSize);
 | |
| 
 | |
|     Random r;
 | |
| 
 | |
|     for (int i = 0; i < dataSize; ++i) {
 | |
|         h[i].uniform = r.uniform();
 | |
|         r.cosHemi(h[i].cosHemiX, h[i].cosHemiY, h[i].cosHemiZ); 
 | |
| 
 | |
|         s[i].bits = r.bits();
 | |
|         r.sphere(s[i].sphereX, s[i].sphereY, s[i].sphereZ);         
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| PrecomputedRandom::PrecomputedRandom(const HemiUniformData* data1, const SphereBitsData* data2, int dataSize, uint32 seed) : 
 | |
|  Random((void*)NULL),
 | |
|  m_hemiUniform(data1),
 | |
|  m_sphereBits(data2),
 | |
|  m_modMask(dataSize - 1),
 | |
|  m_freeData(false) {
 | |
| 
 | |
|     m_index = seed & m_modMask;
 | |
|     alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
 | |
| }
 | |
| 
 | |
| 
 | |
| PrecomputedRandom::~PrecomputedRandom() {
 | |
|     if (m_freeData) {
 | |
|         System::free(const_cast<HemiUniformData*>(m_hemiUniform));
 | |
|         System::free(const_cast<SphereBitsData*>(m_sphereBits));
 | |
|     }
 | |
| }
 | |
| 
 | |
| float PrecomputedRandom::uniform(float low, float high) {
 | |
|     m_index = (m_index + 1) & m_modMask;
 | |
|     return low + m_hemiUniform[m_index].uniform * (high - low);
 | |
| }
 | |
| 
 | |
| 
 | |
| float PrecomputedRandom::uniform() {
 | |
|     m_index = (m_index + 1) & m_modMask;
 | |
|     return m_hemiUniform[m_index].uniform;
 | |
| }
 | |
| 
 | |
| 
 | |
| void PrecomputedRandom::cosHemi(float& x, float& y, float& z) {
 | |
|     m_index = (m_index + 1) & m_modMask;
 | |
|     x = m_hemiUniform[m_index].cosHemiX;
 | |
|     y = m_hemiUniform[m_index].cosHemiY;
 | |
|     z = m_hemiUniform[m_index].cosHemiZ;
 | |
| }
 | |
| 
 | |
| void PrecomputedRandom::cosPowHemi(const float k, float& x, float& y, float& z) {
 | |
|     // Computing a cosPowHemi costs 4 slow functions (pow, sqrt, sin,
 | |
|     // cos). We can do it with two, given a cosHemi sample, basically
 | |
|     // saving the cost of sin and cos and making a single 128-byte
 | |
|     // memory read (for a vector) instead of two (for adjacent uniform
 | |
|     // floats).
 | |
| 
 | |
|     // cos^1 distribution sample
 | |
|     float cos1;
 | |
|     cosHemi(x, y, cos1);
 | |
| 
 | |
|     // Fix the distribution by adjusting the cosine:
 | |
|     // rnd(cos^k t) = (rnd(cos(t))^2)^(1/k)
 | |
|     
 | |
|     // produces cos^k distribution sample
 | |
|     z = pow(cos1, 2.0f / (1.0f + k));
 | |
| 
 | |
|     // Rescale x and y by sqrt(1.0f - square(z)) / sqrt(x*x + y*y).
 | |
|     // Add a very tiny offset to handle the (almost impossibly unlikely) case where
 | |
|     // z = 1 and x^2+y^2 = 0.
 | |
|     static const float eps = 0.000001f;
 | |
|     const float s = sqrt((1.0f + eps - square(z)) / (square(x) + square(y) + eps));
 | |
| 
 | |
|     x *= s;
 | |
|     y *= s;
 | |
| }
 | |
| 
 | |
| 
 | |
| uint32 PrecomputedRandom::bits() {
 | |
|     m_index = (m_index + 1) & m_modMask;
 | |
|     return m_sphereBits[m_index].bits;
 | |
| }
 | |
| 
 | |
| 
 | |
| void PrecomputedRandom::sphere(float& x, float& y, float& z) {
 | |
|     m_index = (m_index + 1) & m_modMask;
 | |
|     x = m_sphereBits[m_index].sphereX;
 | |
|     y = m_sphereBits[m_index].sphereY;
 | |
|     z = m_sphereBits[m_index].sphereZ;
 | |
| }
 | |
| 
 | |
| }
 |