Idea
For a Path Tracing application I need to generate good quality pseudo random numbers in the closed range [0~1]. Because I'm doing this on the GPU (HLSL Shader Model 5) I can only use 32bit variables. My initial approach is the following set-up:
Ever Frame the pixel shader receive a good pseudo random number ([0~1]) from the CPU using C++'s
std::mt19937
generator andstd::uniform_real_distribution
.Because for each pixel this number is the same I also use the screen coordinates
u
andv
of each pixel these are also in [0~1].I then call the Multiply With Carry method like below.
Algorithm
// seed is the value given from the cpu
float3 random = Random(seed + u, seed + v)l
// Multiply With Carry, returns 3 floating point values {x, y, z}
// x: the random number
// y, z: new seeds for the next time we need a random number
float3 Random(float seed_a, float seed_b)
{
uint m_z = asuint(seed_a);
uint m_w = asuint(seed_b);
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
float r = ((m_z << 16) + m_w) / (float)0xFFFFFFFF;
return float3(r, asfloat(m_z), asfloat(m_w));
}
This produces the following output. (The left part is the random number obtained from the Random method for this pixel, the right part is the visualization of u
and v
as Red and Green.
Result
As you can see there is clearly a pattern, so the randomness is not good at all. Which hurts the performance of my algorithm tremendously. This is probably due to the fact that the original Multiply with Carry method assumes m_z
and m_w
are 64 bit integers, not 32 bit ones.
What I want
What I'm looking for is a way to fix my implementation of the Multiply with Carry method so that it produces reasonably good pseudo random numbers and works in the closed [0~1] interval instead of the open [0~1] interval. However since it is quite possible that this method can only work right using 64 bits integers I'm would also be really glad if someone can suggest another pseudo random number generator algorithm that:
- Works with 32 bit numbers
- Produces uniformly distributed results in the closed [0~1] interval
- Does not require too much state information, (this is why I chose MwC since it only needs to store 2 variables) since that is hard on the GPU. 16 32 bit variables would be the maximum I think since I can store that in 4x4 matrix which is easy to pass around.