'How random is glm::sphericalRand()? Some angles seem more common than others

I am following along with Ray Tracing in One Weekend, where Lambert's cosine law is simulated by picking a random point on the surface of a unit sphere and sending a ray in that direction.

The author uses a simple rejection method for generating a random point on the sphere. Since I was already working with GLM, I thought I would be clever and use glm::sphericalRand() instead of doing the work myself. I then decided to see how uniformly distributed the points actually were, and plotted their angles with respect to a normal facing in the y direction:

float buckets[200] = {};

std::srand(time(0)); // Give glm a new seed

for (int i = 0; i < 10000000; i++) {
    const glm::vec3 normal = glm::vec3(0.0f, 1.0f, 0.0f);
    const glm::vec3 randomVector = glm::sphericalRand(1.0f); // Implicitly normalized (sphere radius of 1)
    const float dot = glm::dot(randomVector, normal);

    const int bucket = 100 + dot * 100; // Pick a bucket in the range [0, 199]

    buckets[bucket]++;
}

I generate 10.000.000 random vectors on a unit sphere using glm::sphericalRand(), and take their dot product with the normal. I then increment the bucket corresponding to the value of their dot product.

Each bucket represents the number of vectors within a certain range of angles to the normal. Vectors with a dot product in the range of [-1, -0.99> go in bucket 0, [-0.99, -0.98> in bucket 1, etc.

I expected all angles (buckets) to be chosen approximately equally often. However, this did not seem to be the case. I plotted the number of entries in each bucket below.

enter image description here

Evidently, every bucket has approximately 50.000 entries, which makes sense with 10.000.000 total samples and 200 buckets. However, bucket 99 (corresponding to a dot product of 0) shows a clear dip with approximately half as many samples.

I decided to make my own function to generate a random point on a sphere using this stackexchange comment:

glm::vec3 sphericalRand(float radius = 1.0f) {
    glm::vec3 randomVec = glm::vec3(glm::gaussRand(0.0f, 1.0f), glm::gaussRand(0.0f, 1.0f), glm::gaussRand(0.0f, 1.0f));
    return glm::normalize(randomVec) * radius;
}

Plotting this yielded the following results:

enter image description here

The dip close to a dot product of 0 is less pronounced (though definitely still there). However, some other phenomenon now shows up: there are generally fewer samples with positive dot products (bucket 100 through 199) than there samples with negative dot products (buckets 0 through 99).

Is this a result of how std::rand() works (which I believe is what GLM uses under the hood)? Or is something else going on here?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source